Current section

Files

Jump to
sprocket src sprocket@internal@utils@timer.erl
Raw

src/sprocket@internal@utils@timer.erl

-module(sprocket@internal@utils@timer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([timer/2, interval/2, now/0, convert_time_unit/3, timed_operation/2]).
-export_type([timer_msg/0, timed_operation/0, time_unit/0]).
-type timer_msg() :: cancel |
{timeout, fun(() -> nil)} |
{interval,
gleam@erlang@process:subject(timer_msg()),
integer(),
fun(() -> nil)}.
-type timed_operation() :: {timed_operation, binary(), integer()}.
-type time_unit() :: native | microsecond.
-spec handle_timer_message(timer_msg(), integer()) -> gleam@otp@actor:next(timer_msg(), integer()).
handle_timer_message(Msg, State) ->
case Msg of
cancel ->
{stop, normal};
{timeout, Cb} ->
Cb(),
{stop, normal};
{interval, Actor, Interval_ms, Cb@1} ->
Cb@1(),
gleam@erlang@process:send_after(
Actor,
Interval_ms,
{interval, Actor, Interval_ms, Cb@1}
),
gleam@otp@actor:continue(State)
end.
-spec timer(integer(), fun(() -> nil)) -> gleam@erlang@process:timer().
timer(Interval_ms, Callback) ->
_assert_subject = begin
_pipe = gleam@otp@actor:start(Interval_ms, fun handle_timer_message/2),
gleam@result:map_error(
_pipe,
fun(Error) ->
sprocket@internal@logger:error(
<<"timer.timer: failed to start timer actor"/utf8>>
),
Error
end
)
end,
{ok, Actor} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"sprocket/internal/utils/timer"/utf8>>,
function => <<"timer"/utf8>>,
line => 38})
end,
gleam@erlang@process:send_after(Actor, Interval_ms, {timeout, Callback}).
-spec interval(integer(), fun(() -> nil)) -> fun(() -> nil).
interval(Interval_ms, Callback) ->
_assert_subject = begin
_pipe = gleam@otp@actor:start(Interval_ms, fun handle_timer_message/2),
gleam@result:map_error(
_pipe,
fun(Error) ->
sprocket@internal@logger:error(
<<"timer.interval: failed to start timer actor"/utf8>>
),
Error
end
)
end,
{ok, Actor} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"sprocket/internal/utils/timer"/utf8>>,
function => <<"interval"/utf8>>,
line => 50})
end,
gleam@erlang@process:send_after(
Actor,
Interval_ms,
{interval, Actor, Interval_ms, Callback}
),
fun() -> gleam@otp@actor:send(Actor, cancel) end.
-spec now() -> integer().
now() ->
erlang:monotonic_time().
-spec begin_timed_operation(binary()) -> timed_operation().
begin_timed_operation(Label) ->
sprocket@internal@logger:debug(
gleam@string:concat([<<"Starting "/utf8>>, Label, <<"..."/utf8>>])
),
{timed_operation, Label, erlang:monotonic_time()}.
-spec convert_time_unit(integer(), time_unit(), time_unit()) -> integer().
convert_time_unit(A, B, C) ->
erlang:convert_time_unit(A, B, C).
-spec complete_timed_operation(timed_operation()) -> integer().
complete_timed_operation(Op) ->
Elapsed = erlang:convert_time_unit(
erlang:monotonic_time() - erlang:element(3, Op),
native,
microsecond
),
Formatted_elapsed = case Elapsed > 1000 of
true ->
gleam@string:append(
gleam@int:to_string(Elapsed div 1000),
<<"ms"/utf8>>
);
false ->
gleam@string:append(gleam@int:to_string(Elapsed), <<"µs"/utf8>>)
end,
_pipe = gleam@string:concat(
[erlang:element(2, Op), <<" completed in "/utf8>>, Formatted_elapsed]
),
sprocket@internal@logger:debug(_pipe),
Elapsed.
-spec timed_operation(binary(), fun(() -> JFJ)) -> JFJ.
timed_operation(Label, Cb) ->
Op_timer = begin_timed_operation(Label),
Result = Cb(),
complete_timed_operation(Op_timer),
Result.