-module(aion@workflow@timer). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/aion/workflow/timer.gleam"). -export([timer_id/1, sleep/1, start_timer/2, cancel_timer/1, with_timeout/2]). -export_type([timer_ref/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(" Durable workflow timers over canonical `Duration` values.\n"). -opaque timer_ref() :: {timer_ref, binary()}. -file("src/aion/workflow/timer.gleam", 20). ?DOC(" Return the engine timer identifier carried by a named timer reference.\n"). -spec timer_id(timer_ref()) -> binary(). timer_id(Reference) -> erlang:element(2, Reference). -file("src/aion/workflow/timer.gleam", 105). -spec duration_to_boundary(aion@duration:duration()) -> binary(). duration_to_boundary(Duration) -> _pipe = Duration, _pipe@1 = aion@duration:to_milliseconds(_pipe), erlang:integer_to_binary(_pipe@1). -file("src/aion/workflow/timer.gleam", 34). ?DOC( " Wait for an anonymous durable timer to fire.\n" "\n" " The timer is durable: it survives engine restart and, during replay, returns\n" " instantly once the matching timer-fired event is already present in history.\n" " Anonymous sleeps are not separately cancellable; cancelling a sleep means\n" " cancelling the workflow that is blocked on it (AT D3). Use `start_timer` when\n" " workflow code needs a named timer that can be cancelled independently.\n" " The await is a yield point: pending workflow queries are serviced by the\n" " query pump while the timer is parked. `with_timeout` needs no pump of its\n" " own — the awaits running inside its operation are the yield points.\n" ). -spec sleep(aion@duration:duration()) -> {ok, nil} | {error, aion@error:engine_error()}. sleep(Duration) -> Boundary = duration_to_boundary(Duration), case aion@internal@pump:run( fun() -> aion@internal@pump:shield(aion_flow_ffi:sleep(Boundary)) end ) of {ok, _} -> {ok, nil}; {error, Raw_error} -> {error, {engine_failure, Raw_error}} end. -file("src/aion/workflow/timer.gleam", 52). ?DOC( " Start a named durable timer and return its cancellable reference.\n" "\n" " The supplied `name` is the named timer identifier handed to AT. The SDK does\n" " not invent a default duration or rewrite the identifier; engine-side timer ID\n" " validation remains authoritative.\n" ). -spec start_timer(binary(), aion@duration:duration()) -> {ok, timer_ref()} | {error, aion@error:engine_error()}. start_timer(Name, Duration) -> case aion_flow_ffi:start_timer(Name, duration_to_boundary(Duration)) of {ok, Timer_id} -> {ok, {timer_ref, Timer_id}}; {error, Raw_error} -> {error, {engine_failure, Raw_error}} end. -file("src/aion/workflow/timer.gleam", 68). ?DOC( " Cancel a named durable timer.\n" "\n" " AT treats cancelling an already-fired or already-cancelled named timer as an\n" " idempotent no-op, so a successful engine response is always returned as\n" " `Ok(Nil)`. Anonymous sleeps cannot be cancelled through this function because\n" " they never produce a `TimerRef`.\n" ). -spec cancel_timer(timer_ref()) -> {ok, nil} | {error, aion@error:engine_error()}. cancel_timer(Reference) -> case aion_flow_ffi:cancel_timer(erlang:element(2, Reference)) of {ok, _} -> {ok, nil}; {error, Raw_error} -> {error, {engine_failure, Raw_error}} end. -file("src/aion/workflow/timer.gleam", 85). ?DOC( " Run an awaiting operation with a durable deadline.\n" "\n" " The operation is a thunk so the engine/test FFI can establish the timeout\n" " before the await begins. If the operation completes before the deadline its\n" " `Ok` value is returned. If the operation returns its own typed error, that\n" " error is wrapped in `InnerError`. If AT reports that the deadline expired\n" " (the engine's `timeout:`-tagged result), the result is\n" " `TimedOutError(TimedOut(...))`. Any other engine error is surfaced as\n" " `TimeoutEngineFailure` — an infrastructure fault must never be mistaken\n" " for a deadline expiry.\n" ). -spec with_timeout( fun(() -> {ok, FJH} | {error, FJI}), aion@duration:duration() ) -> {ok, FJH} | {error, aion@error:timeout_result_error(FJI)}. with_timeout(Operation, Deadline) -> case aion_flow_ffi:with_timeout(duration_to_boundary(Deadline), Operation) of {ok, {ok, Value}} -> {ok, Value}; {ok, {error, Inner_error}} -> {error, {inner_error, Inner_error}}; {error, Raw_error} -> case gleam_stdlib:string_starts_with(Raw_error, <<"timeout:"/utf8>>) of true -> {error, {timed_out_error, {timed_out, gleam@string:drop_start(Raw_error, 8)}}}; false -> {error, {timeout_engine_failure, Raw_error}} end end.