Current section
Files
Jump to
Current section
Files
src/chip.erl
-module(chip).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([new/1, tag/2, group/2, register/2, dispatch/2, dispatch_group/3, stop/1, find/2, start/0]).
-export_type([message/3, chip/3, state/3, process_down/0, table/0, option/0, step/0, search/1]).
-opaque message(HNE, HNF, HNG) :: {register, chip(HNE, HNF, HNG)} |
{demonitor, gleam@erlang:reference_(), gleam@erlang@process:pid_()} |
{find, gleam@erlang@process:subject(gleam@erlang:reference_())} |
{dispatch, fun((gleam@erlang@process:subject(HNE)) -> nil)} |
{dispatch_group, fun((gleam@erlang@process:subject(HNE)) -> nil), HNG} |
{no_operation, gleam@dynamic:dynamic_()} |
stop.
-opaque chip(HNH, HNI, HNJ) :: {chip,
gleam@erlang@process:subject(HNH),
gleam@option:option(HNI),
gleam@option:option(HNJ)}.
-type state(HNK, HNL, HNM) :: {state,
integer(),
gleam@erlang:reference_(),
gleam@erlang:reference_(),
gleam@erlang:reference_()} |
{gleam_phantom, HNK, HNL, HNM}.
-type process_down() :: {process_down,
gleam@erlang:reference_(),
gleam@erlang@process:pid_()}.
-type table() :: chip_registry | chip_registry_tagged | chip_registry_grouped.
-type option() :: protected | set | bag.
-type step() :: any().
-type search(HNN) :: {partial, list(HNN), step()} | {end_of_table, list(HNN)}.
-spec new(gleam@erlang@process:subject(HOD)) -> chip(HOD, any(), any()).
new(Subject) ->
{chip, Subject, none, none}.
-spec tag(chip(HOK, HOL, HOM), HOL) -> chip(HOK, HOL, HOM).
tag(Registrant, Tag) ->
erlang:setelement(3, Registrant, {some, Tag}).
-spec group(chip(HOT, HOU, HOV), HOV) -> chip(HOT, HOU, HOV).
group(Registrant, Group) ->
erlang:setelement(4, Registrant, {some, Group}).
-spec register(
gleam@erlang@process:subject(message(HPC, HPD, HPE)),
chip(HPC, HPD, HPE)
) -> nil.
register(Registry, Registrant) ->
gleam@erlang@process:send(Registry, {register, Registrant}).
-spec dispatch(
gleam@erlang@process:subject(message(HPV, any(), any())),
fun((gleam@erlang@process:subject(HPV)) -> nil)
) -> nil.
dispatch(Registry, Callback) ->
gleam@erlang@process:send(Registry, {dispatch, Callback}).
-spec dispatch_group(
gleam@erlang@process:subject(message(HQC, any(), HQE)),
HQE,
fun((gleam@erlang@process:subject(HQC)) -> nil)
) -> nil.
dispatch_group(Registry, Group, Callback) ->
gleam@erlang@process:send(Registry, {dispatch_group, Callback, Group}).
-spec stop(gleam@erlang@process:subject(message(any(), any(), any()))) -> nil.
stop(Registry) ->
gleam@erlang@process:send(Registry, stop).
-spec run_batch(
list(gleam@erlang@process:subject(HSV)),
fun((gleam@erlang@process:subject(HSV)) -> nil)
) -> nil.
run_batch(Subjects, Callback) ->
_pipe = Subjects,
_pipe@1 = gleam@list:map(
_pipe,
fun(Subject) -> gleam@otp@task:async(fun() -> Callback(Subject) end) end
),
gleam@list:each(_pipe@1, fun(Task) -> gleam@otp@task:await(Task, 5000) end).
-spec match_into(integer()) -> gleam@erlang@atom:atom_().
match_into(N) ->
erlang:binary_to_atom(<<"$"/utf8, (gleam@int:to_string(N))/binary>>).
-spec match_any() -> gleam@erlang@atom:atom_().
match_any() ->
erlang:binary_to_atom(<<"_"/utf8>>).
-spec insert(state(HRR, HRS, HRT), chip(HRR, HRS, HRT)) -> nil.
insert(State, Registrant) ->
Pid = gleam@erlang@process:subject_owner(erlang:element(2, Registrant)),
_ = gleam@erlang@process:monitor_process(Pid),
_assert_subject = ets:insert(
erlang:element(3, State),
{erlang:element(2, Registrant), Pid}
),
true = case _assert_subject of
true -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"chip"/utf8>>,
function => <<"insert"/utf8>>,
line => 342})
end,
gleam@option:map(
erlang:element(3, Registrant),
fun(Tag) ->
_assert_subject@1 = ets:insert(
erlang:element(4, State),
{Tag, Pid, erlang:element(2, Registrant)}
),
true = case _assert_subject@1 of
true -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"chip"/utf8>>,
function => <<"insert"/utf8>>,
line => 345})
end
end
),
gleam@option:map(
erlang:element(4, Registrant),
fun(Group) ->
_assert_subject@2 = ets:insert(
erlang:element(5, State),
{Group, Pid, erlang:element(2, Registrant)}
),
true = case _assert_subject@2 of
true -> _assert_subject@2;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@2,
module => <<"chip"/utf8>>,
function => <<"insert"/utf8>>,
line => 349})
end
end
),
nil.
-spec find(gleam@erlang@process:subject(message(HPL, any(), any())), any()) -> {ok,
gleam@erlang@process:subject(HPL)} |
{error, nil}.
find(Registry, Tag) ->
Table = gleam@erlang@process:call(
Registry,
fun(_capture) -> {find, _capture} end,
500
),
case ets:lookup(Table, Tag) of
[{_, _, Subject}] ->
{ok, Subject};
[] ->
{error, nil};
_ ->
erlang:error(#{gleam_error => panic,
message => <<"Impossible lookup on a tagged table."/utf8>>,
module => <<"chip"/utf8>>,
function => <<"find"/utf8>>,
line => 143})
end.
-spec delete(
state(any(), any(), any()),
gleam@erlang:reference_(),
gleam@erlang@process:pid_()
) -> nil.
delete(State, Monitor, Pid) ->
nil = chip_erlang_ffi:demonitor(Monitor),
_assert_subject = ets:match_delete(
erlang:element(3, State),
{match_any(), Pid}
),
true = case _assert_subject of
true -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"chip"/utf8>>,
function => <<"delete"/utf8>>,
line => 363})
end,
_assert_subject@1 = ets:match_delete(
erlang:element(4, State),
{match_any(), Pid, match_any()}
),
true = case _assert_subject@1 of
true -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"chip"/utf8>>,
function => <<"delete"/utf8>>,
line => 364})
end,
_assert_subject@2 = ets:match_delete(
erlang:element(5, State),
{match_any(), Pid, match_any()}
),
true = case _assert_subject@2 of
true -> _assert_subject@2;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@2,
module => <<"chip"/utf8>>,
function => <<"delete"/utf8>>,
line => 366})
end,
nil.
-spec init() -> gleam@otp@actor:init_result(state(HQP, HQQ, HQR), message(HQP, HQQ, HQR)).
init() ->
Process_down = fun(Message) ->
case chip_erlang_ffi:decode_down_message(Message) of
{ok, {process_down, Monitor, Pid}} ->
{demonitor, Monitor, Pid};
{error, nil} ->
{no_operation, Message}
end
end,
{ready,
{state,
chip_erlang_ffi:schedulers(),
ets:new(chip_registry, [protected, set]),
ets:new(chip_registry_tagged, [protected, set]),
ets:new(chip_registry_grouped, [protected, bag])},
begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting_anything(_pipe, Process_down)
end}.
-spec continue_dispatch(
step(),
fun((any()) -> gleam@erlang@process:subject(HSM)),
fun((gleam@erlang@process:subject(HSM)) -> nil)
) -> nil.
continue_dispatch(Step, Decode_record, Task) ->
_pipe = chip_erlang_ffi:search(Step),
handle_dispatch_results(_pipe, Decode_record, Task).
-spec handle_dispatch_results(
search(HSP),
fun((HSP) -> gleam@erlang@process:subject(HSR)),
fun((gleam@erlang@process:subject(HSR)) -> nil)
) -> nil.
handle_dispatch_results(Lookup, Decode_record, Task) ->
case Lookup of
{partial, Objects, Step} ->
_pipe = Objects,
_pipe@1 = gleam@list:map(_pipe, Decode_record),
run_batch(_pipe@1, Task),
continue_dispatch(Step, Decode_record, Task);
{end_of_table, Objects@1} ->
_pipe@2 = Objects@1,
_pipe@3 = gleam@list:map(_pipe@2, Decode_record),
run_batch(_pipe@3, Task)
end.
-spec start_dispatch(
gleam@erlang:reference_(),
any(),
fun((any()) -> gleam@erlang@process:subject(HSI)),
fun((gleam@erlang@process:subject(HSI)) -> nil),
integer()
) -> nil.
start_dispatch(Table, Pattern, Decode_record, Task, Concurrency) ->
gleam@erlang@process:start(fun() -> _pipe = Table,
_pipe@1 = chip_erlang_ffi:search(_pipe, Pattern, Concurrency),
handle_dispatch_results(_pipe@1, Decode_record, Task) end, false),
nil.
-spec loop(message(HRA, HRB, HRC), state(HRA, HRB, HRC)) -> gleam@otp@actor:next(message(HRA, HRB, HRC), state(HRA, HRB, HRC)).
loop(Message, State) ->
case Message of
{register, Registrant} ->
nil = insert(State, Registrant),
{continue, State, none};
{demonitor, Monitor, Pid} ->
nil = delete(State, Monitor, Pid),
{continue, State, none};
{find, Client} ->
gleam@erlang@process:send(Client, erlang:element(4, State)),
{continue, State, none};
{dispatch, Callback} ->
Match = {match_into(1), match_any()},
Cast = fun(Object) ->
[Subject] = case Object of
[_] -> Object;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"chip"/utf8>>,
function => <<"loop"/utf8>>,
line => 287})
end,
Subject
end,
start_dispatch(
erlang:element(3, State),
Match,
Cast,
Callback,
erlang:element(2, State)
),
{continue, State, none};
{dispatch_group, Callback@1, Group} ->
Match@1 = {Group, match_any(), match_into(1)},
Cast@1 = fun(Object@1) ->
[Subject@1] = case Object@1 of
[_] -> Object@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"chip"/utf8>>,
function => <<"loop"/utf8>>,
line => 305})
end,
Subject@1
end,
start_dispatch(
erlang:element(5, State),
Match@1,
Cast@1,
Callback@1,
erlang:element(2, State)
),
{continue, State, none};
{no_operation, Message@1} ->
gleam@io:println(
<<"chip: received an out of bound message from a non-selected process.\n"/utf8,
(gleam@string:inspect(Message@1))/binary>>
),
{continue, State, none};
stop ->
{stop, normal}
end.
-spec start() -> {ok,
gleam@erlang@process:subject(message(any(), any(), any()))} |
{error, gleam@otp@actor:start_error()}.
start() ->
gleam@otp@actor:start_spec({spec, fun init/0, 10, fun loop/2}).