Packages

Lightweight server behaviour

Current section

Files

Jump to
metal src metal.erl
Raw

src/metal.erl

-module(metal).
-compile(inline).
-compile({inline_size, 512}).
%% internal
-export([
init/4,
start_link/3
]).
%% sys behavior
-export([
system_code_change/4,
system_continue/3,
system_get_state/1,
system_terminate/4
]).
%% callbacks
-callback init(Name :: atom(), Parent :: pid(), Opts :: term()) ->
{ok, State :: term()} | {stop, Reason :: atom()}.
-callback handle_msg(Msg :: term(), State :: term()) ->
{ok, State :: term()}.
-callback terminate(Reason :: term(), State :: term()) ->
ok.
-optional_callbacks([
init/3,
terminate/2
]).
%% public
-spec start_link(module(), atom(), term()) ->
{ok, pid()}.
start_link(Module, Name, State) ->
proc_lib:start_link(?MODULE, init, [Module, Name, self(), State]).
-spec init(module(), atom(), pid(), term()) ->
no_return() | ok.
init(Module, Name, Parent, State) ->
case safe_register(Name) of
true ->
process_flag(trap_exit, true),
module_init(Module, Name, Parent, State);
{false, Pid} ->
proc_lib:init_ack(Parent, {error, {already_started, Pid}})
end.
%% sys callbacks
-spec system_code_change(term(), module(), undefined | term(), term()) ->
{ok, term()}.
system_code_change(State, _Module, _OldVsn, _Extra) ->
{ok, State}.
-spec system_continue(pid(), [], {module(), atom(), pid(), term()}) ->
ok.
system_continue(_Parent, _Debug, {Module, Name, Parent, State}) ->
loop(Module, Name, Parent, State).
-spec system_get_state(term()) ->
{ok, term()}.
system_get_state(State) ->
{ok, State}.
-spec system_terminate(term(), pid(), [], term()) ->
none().
system_terminate(Reason, _Parent, _Debug, _State) ->
exit(Reason).
%% private
loop(Module, Name, Parent, State) ->
receive
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent, ?MODULE, [],
{Module, Name, Parent, State});
{'EXIT', Parent, Reason} ->
terminate(Module, Reason, State);
Msg ->
{ok, State2} = Module:handle_msg(Msg, State),
loop(Module, Name, Parent, State2)
end.
module_init(Module, Name, Parent, State) ->
case erlang:function_exported(Module, init, 3) of
true ->
case Module:init(Name, Parent, State) of
{ok, State2} ->
proc_lib:init_ack(Parent, {ok, self()}),
loop(Module, Name, Parent, State2);
{stop, Reason} ->
proc_lib:init_ack(Parent, {error, Reason}),
exit(Reason)
end;
false ->
proc_lib:init_ack(Parent, {ok, self()}),
loop(Module, Name, Parent, State)
end.
safe_register(Name) ->
try register(Name, self()) of
true -> true
catch
_:_ -> {false, whereis(Name)}
end.
terminate(Module, Reason, State) ->
ok = case erlang:function_exported(Module, terminate, 2) of
true ->
Module:terminate(Reason, State);
false ->
ok
end,
exit(Reason).