-module(singleflight). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/singleflight.gleam"). -export([config/2, fetch/3, start/2]). -export_type([config/0, message/2, singleflight/2, state/2]). -type config() :: {config, integer(), integer()}. -type message(FLZ, FMA) :: {request, FLZ, fun((FLZ) -> FMA), gleam@erlang@process:subject(FMA)} | {done, FLZ, FMA}. -opaque singleflight(FMB, FMC) :: {singleflight, gleam@erlang@process:subject(message(FMB, FMC)), integer()}. -type state(FMD, FME) :: {state, gleam@dict:dict(FMD, list(gleam@erlang@process:subject(FME))), gleam@erlang@process:subject(message(FMD, FME))}. -file("src/singleflight.gleam", 26). -spec config(integer(), integer()) -> config(). config(Initialisation_timeout_ms, Fetch_timeout_ms) -> {config, Initialisation_timeout_ms, Fetch_timeout_ms}. -file("src/singleflight.gleam", 52). -spec fetch(singleflight(FMN, FMO), FMN, fun((FMN) -> FMO)) -> FMO. fetch(Singleflight, Key, Work) -> {singleflight, Subject, Fetch_timeout_ms} = Singleflight, gleam@otp@actor:call( Subject, Fetch_timeout_ms, fun(Caller) -> {request, Key, Work, Caller} end ). -file("src/singleflight.gleam", 60). -spec handle_message(state(FMR, FMS), message(FMR, FMS)) -> gleam@otp@actor:next(state(FMR, FMS), message(FMR, FMS)). handle_message(State, Message) -> case Message of {request, Key, Work, Caller} -> case gleam_stdlib:map_get(erlang:element(2, State), Key) of {ok, Waiters} -> gleam@otp@actor:continue( {state, gleam@dict:insert( erlang:element(2, State), Key, [Caller | Waiters] ), erlang:element(3, State)} ); {error, nil} -> Self = erlang:element(3, State), proc_lib:spawn_link( fun() -> Result = Work(Key), gleam@otp@actor:send(Self, {done, Key, Result}) end ), gleam@otp@actor:continue( {state, gleam@dict:insert( erlang:element(2, State), Key, [Caller] ), erlang:element(3, State)} ) end; {done, Key@1, Result@1} -> case gleam_stdlib:map_get(erlang:element(2, State), Key@1) of {ok, Waiters@1} -> gleam@list:each( Waiters@1, fun(Waiter) -> gleam@erlang@process:send(Waiter, Result@1) end ); {error, nil} -> nil end, gleam@otp@actor:continue( {state, gleam@dict:delete(erlang:element(2, State), Key@1), erlang:element(3, State)} ) end. -file("src/singleflight.gleam", 33). -spec start(config(), gleam@erlang@process:name(message(FMF, FMG))) -> {ok, gleam@otp@actor:started(singleflight(FMF, FMG))} | {error, gleam@otp@actor:start_error()}. start(Config, Name) -> {config, Initialisation_timeout_ms, Fetch_timeout_ms} = Config, _pipe@2 = gleam@otp@actor:new_with_initialiser( Initialisation_timeout_ms, fun(Self) -> _pipe = gleam@otp@actor:initialised({state, maps:new(), Self}), _pipe@1 = gleam@otp@actor:returning( _pipe, {singleflight, Self, Fetch_timeout_ms} ), {ok, _pipe@1} end ), _pipe@3 = gleam@otp@actor:on_message(_pipe@2, fun handle_message/2), _pipe@4 = gleam@otp@actor:named(_pipe@3, Name), gleam@otp@actor:start(_pipe@4).