-module(gleam@otp@actor). -compile(no_auto_import). -export([to_erlang_start_result/1, start_spec/1, start/2, send/2, call/3]). -export_type([message/1, next/1, init_result/2, self/2, spec/2, start_error/0, start_init_message/1]). -type message(ETB) :: {message, ETB} | {system, gleam@otp@system:system_message()} | {unexpected, gleam@dynamic:dynamic()}. -type next(ETC) :: {continue, ETC} | {stop, gleam@erlang@process:exit_reason()}. -type init_result(ETD, ETE) :: {ready, ETD, gleam@erlang@process:selector(ETE)} | {failed, binary()}. -type self(ETF, ETG) :: {self, gleam@otp@system:mode(), gleam@erlang@process:pid_(), ETF, gleam@erlang@process:selector(message(ETG)), gleam@otp@system:debug_state(), fun((ETG, ETF) -> next(ETF))}. -type spec(ETH, ETI) :: {spec, fun(() -> init_result(ETH, ETI)), integer(), fun((ETI, ETH) -> next(ETH))}. -type start_error() :: init_timeout | {init_failed, gleam@erlang@process:exit_reason()} | {init_crashed, gleam@dynamic:dynamic()}. -type start_init_message(ETP) :: {ack, {ok, gleam@erlang@process:subject(ETP)} | {error, gleam@erlang@process:exit_reason()}} | {mon, gleam@erlang@process:process_down()}. -spec exit_process(gleam@erlang@process:exit_reason()) -> gleam@erlang@process:exit_reason(). exit_process(Reason) -> Reason. -spec receive_message(self(any(), EUE)) -> message(EUE). receive_message(Self) -> Selector = case erlang:element(2, Self) of suspended -> _pipe = gleam@erlang@process:new_selector(), selecting_system_messages(_pipe); running -> _pipe@1 = gleam@erlang@process:new_selector(), _pipe@2 = gleam@erlang@process:selecting_anything( _pipe@1, fun(A) -> {unexpected, A} end ), _pipe@3 = gleam@erlang@process:merge_selector( _pipe@2, erlang:element(5, Self) ), selecting_system_messages(_pipe@3) end, gleam@erlang@process:select_forever(Selector). -spec selecting_system_messages(gleam@erlang@process:selector(message(EUI))) -> gleam@erlang@process:selector(message(EUI)). selecting_system_messages(Selector) -> _pipe = Selector, gleam@erlang@process:selecting_record3( _pipe, gleam@erlang@atom:create_from_string(<<"system"/utf8>>), fun gleam_otp_external:convert_system_message/2 ). -spec process_status_info(self(any(), any())) -> gleam@otp@system:status_info(). process_status_info(Self) -> {status_info, gleam@erlang@atom:create_from_string(<<"gleam@otp@actor"/utf8>>), erlang:element(3, Self), erlang:element(2, Self), erlang:element(6, Self), gleam@dynamic:from(erlang:element(4, Self))}. -spec loop(self(any(), any())) -> gleam@erlang@process:exit_reason(). loop(Self) -> case receive_message(Self) of {system, System} -> case System of {get_state, Callback} -> Callback(gleam@dynamic:from(erlang:element(4, Self))), loop(Self); {resume, Callback@1} -> Callback@1(), loop(erlang:setelement(2, Self, running)); {suspend, Callback@2} -> Callback@2(), loop(erlang:setelement(2, Self, suspended)); {get_status, Callback@3} -> Callback@3(process_status_info(Self)), loop(Self) end; {unexpected, Message} -> logger:warning( gleam@erlang@charlist:from_string( <<"Actor discarding unexpected message: ~s"/utf8>> ), [gleam@erlang@charlist:from_string( gleam@string:inspect(Message) )] ), loop(Self); {message, Msg} -> case (erlang:element(7, Self))(Msg, erlang:element(4, Self)) of {stop, Reason} -> exit_process(Reason); {continue, State} -> loop(erlang:setelement(4, Self, State)) end end. -spec initialise_actor( spec(any(), EUZ), gleam@erlang@process:subject({ok, gleam@erlang@process:subject(EUZ)} | {error, gleam@erlang@process:exit_reason()}) ) -> gleam@erlang@process:exit_reason(). initialise_actor(Spec, Ack) -> Subject = gleam@erlang@process:new_subject(), case (erlang:element(2, Spec))() of {ready, State, Selector} -> Selector@1 = begin _pipe = gleam@erlang@process:new_selector(), _pipe@1 = gleam@erlang@process:selecting( _pipe, Subject, fun(A) -> {message, A} end ), gleam@erlang@process:merge_selector( _pipe@1, gleam@erlang@process:map_selector( Selector, fun(A) -> {message, A} end ) ) end, gleam@erlang@process:send(Ack, {ok, Subject}), Self = {self, running, gleam@erlang@process:subject_owner(Ack), State, Selector@1, gleam@otp@system:debug_state([]), erlang:element(4, Spec)}, loop(Self); {failed, Reason} -> gleam@erlang@process:send(Ack, {error, {abnormal, Reason}}), exit_process({abnormal, Reason}) end. -spec to_erlang_start_result( {ok, gleam@erlang@process:subject(any())} | {error, start_error()} ) -> {ok, gleam@erlang@process:pid_()} | {error, gleam@dynamic:dynamic()}. to_erlang_start_result(Res) -> case Res of {ok, X} -> {ok, gleam@erlang@process:subject_owner(X)}; {error, X@1} -> {error, gleam@dynamic:from(X@1)} end. -spec start_spec(spec(any(), EVN)) -> {ok, gleam@erlang@process:subject(EVN)} | {error, start_error()}. start_spec(Spec) -> Ack_subject = gleam@erlang@process:new_subject(), Child = gleam@erlang@process:start( fun() -> initialise_actor(Spec, Ack_subject) end, true ), Monitor = gleam@erlang@process:monitor_process(Child), Selector = begin _pipe = gleam@erlang@process:new_selector(), _pipe@1 = gleam@erlang@process:selecting( _pipe, Ack_subject, fun(A) -> {ack, A} end ), gleam@erlang@process:selecting_process_down( _pipe@1, Monitor, fun(A) -> {mon, A} end ) end, Result = case gleam@erlang@process:select(Selector, erlang:element(3, Spec)) of {ok, {ack, {ok, Channel}}} -> {ok, Channel}; {ok, {ack, {error, Reason}}} -> {error, {init_failed, Reason}}; {ok, {mon, Down}} -> {error, {init_crashed, erlang:element(3, Down)}}; {error, nil} -> gleam@erlang@process:kill(Child), {error, init_timeout} end, gleam@erlang@process:demonitor_process(Monitor), Result. -spec start(EVT, fun((EVU, EVT) -> next(EVT))) -> {ok, gleam@erlang@process:subject(EVU)} | {error, start_error()}. start(State, Loop) -> start_spec( {spec, fun() -> {ready, State, gleam@erlang@process:new_selector()} end, 5000, Loop} ). -spec send(gleam@erlang@process:subject(EVZ), EVZ) -> nil. send(Subject, Msg) -> gleam@erlang@process:send(Subject, Msg). -spec call( gleam@erlang@process:subject(EWB), fun((gleam@erlang@process:subject(EWD)) -> EWB), integer() ) -> EWD. call(Selector, Make_message, Timeout) -> gleam@erlang@process:call(Selector, Make_message, Timeout).