diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 11:06:49 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 11:06:49 +0200 |
| commit | e36e69073e209c7e3db2f6754e1d04bdc3a3c5ca (patch) | |
| tree | 2ba74b6097cd74bdc676ab2006286d7debfb6434 | |
| parent | d1edd420463cdda399d8acb8124b137cbfd7a898 (diff) | |
Read repeat clock
| -rw-r--r-- | lua/clever_f/repeat_resolver.lua | 42 | ||||
| -rw-r--r-- | tests/run.lua | 24 |
2 files changed, 66 insertions, 0 deletions
diff --git a/lua/clever_f/repeat_resolver.lua b/lua/clever_f/repeat_resolver.lua index 5e1dc41..c62a86f 100644 --- a/lua/clever_f/repeat_resolver.lua +++ b/lua/clever_f/repeat_resolver.lua @@ -34,6 +34,22 @@ local function require_policy(service) return service end +local function require_clock(clock) + if clock ~= nil and (type(clock) ~= "table" + or type(clock.read_time_ms) ~= "function") + then + fail("RepeatResolver clock must provide read_time_ms", 3) + end + return clock +end + +local function finite_time(value) + return type(value) == "number" + and value == value + and value > -math.huge + and value < math.huge +end + local resolver_metatable = { __index = RepeatResolver, __newindex = function() @@ -64,6 +80,7 @@ function RepeatResolver.new(options) local resolver = setmetatable({}, resolver_metatable) resolver_records[resolver] = { + clock = require_clock(options.clock or options.time_provider or options.host), policy = require_policy(options.policy or options.policy_service), state = state, } @@ -109,6 +126,27 @@ end RepeatResolver.sample_repeat_timeout = RepeatResolver.sample_repeat_timeout_ms +function RepeatResolver:evaluate_timeout() + local timeout = self:sample_repeat_timeout_ms() + if timeout == 0 then + return M.Decision.REPEAT, nil + end + + local clock = resolver_records[self].clock + if clock == nil then + fail("RepeatResolver requires a clock for positive repeat timeout", 2) + end + local current_time = clock:read_time_ms() + if not finite_time(current_time) then + fail("repeat clock must return finite milliseconds", 2) + end + local elapsed_ms = current_time - resolver_records[self].state.repeat_timestamp_ms + return nil, elapsed_ms +end + +RepeatResolver.check_timeout = RepeatResolver.evaluate_timeout +RepeatResolver.resolve_timeout = RepeatResolver.evaluate_timeout + function M.new(options) return RepeatResolver.new(options) end @@ -131,6 +169,10 @@ function M.sample_repeat_timeout_ms(options) return RepeatResolver.new(options):sample_repeat_timeout_ms() end +function M.evaluate_timeout(options) + return RepeatResolver.new(options):evaluate_timeout() +end + setmetatable(M, { __call = function(_, options) return RepeatResolver.new(options) diff --git a/tests/run.lua b/tests/run.lua index a2bf7e1..437a3ba 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -3860,6 +3860,30 @@ test("Repeated primary timeout is sampled from live policy", function() end, "requires a policy") end) +test("Positive repeat timeout reads the current clock", function() + local _, transitions = fresh_sequence_state() + transitions:SetRepeatTimestamp(25.25) + local host = MemoryHost.new({ + configuration = { repeat_timeout_ms = 100 }, + time_values_ms = { 125.75 }, + }) + local resolver = repeat_resolver.new({ + clock = host, + policy = policy.new(host), + }) + + local decision, elapsed_ms = resolver:evaluate_timeout() + same(nil, decision) + same(100.5, elapsed_ms) + local reads = 0 + for _, operation in ipairs(host:operations()) do + if operation.operation == "read_time_ms" then + reads = reads + 1 + end + end + same(1, reads) +end) + for _, item in ipairs(tests) do local ok, failure = xpcall(item.body, debug.traceback) if not ok then |
