-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.