This section describes how to write a process which understands and behaves like the generic processes, for example gen_server
and gen_fsm
. In this context, behaves means:
All processes should be started in a supervision tree and they must respond to system messages when started this way.
System messages are used to change code in a controlled way and they are synchronized by a dedicated process which is called the release handler. Other typical system messages are requests for process status, and requests to suspend or resume process execution and debug messages.
The proc_lib
module should be used to start a process. This process wraps the initial function call with a catch
, and a crash report is generated if the process terminates with another reason than normal
or shutdown
.
The function which starts a new process shall always return {ok,Pid}
when successfully started, or {error,Reason}
in case of failure. One of the proc_lib:start_link
or spawn_link
functions must be used when the process is included in a supervision tree . The following simple example illustrates this:
-module(test). -export([start/0,init/1]). start() -> case whereis(test_server) of undefined -> Pid = proc_lib:spawn_link(test, init, [self()]), {ok, Pid}; Pid -> {error, {already_started, Pid}} end. init(Parent) -> register(test_server, self( )), %% here is the new process.
System messages are received as: {system, From, Request}
. The content and meaning of this message are not interpreted by the receiving process module. When a system message has been received the function sys:handle_system_msg(Request, From, Parent, Mod, Deb, Misc)
is called in order to handle the request. The arguments of this function have the following meaning:
Request
is any term
From
is the process identity of the calling process
Parent
is the parent process identity
Mod
is the current module
Deb
is a list of debug information
Misc
is any term describing the internal state.
![]() |
The |
The Mod
module must export the following functions, which may be called from the sys:handle_system_msg/6
function:
system_continue(Parent, Deb, Misc)
, where Misc
is the internal state (i.e. loop data) forwarded in the above call to handle_system_msg/6
. This function resumes normal execution. .
system_terminate(Reason, Parent, Deb, Misc)
. The Parent
process has terminated with Reason
, or ordered us to terminate according to the shutdown protocol. This provides a chance to clean up before terminating.
system_code_change(Misc, OldVsn, Module, Extra) -> {ok, NMisc} | Error
. In this case, our process has been ordered to perform a code change. Extra
gives extra information about the code change. OldVsn
is the old version of Module
. The system_code_change
function is executed in the newly loaded version of the module. This function should return the internal state, possibly modified in order to fulfil the needs of the new module.
![]() |
The version is specified as an attribute |
According to the shutdown protocol,
a {'EXIT',Parent,Reason}
message from Parent
is an order to terminate. Normally one shall terminate the process with the same Reason
as Parent
.
If the modules used to implement the process can change dynamically during runtime, there is one more message a process must understand. An example is the gen_event
processes, which add new handlers at runtime.
This message is {get_modules, From}
. The reply to this message is {modules, Modules}
, where Modules
is a list of the currently active modules in the process.
This message is used by the release handler to find which processes execute a certain module. A process may then be suspended and ordered to perform a code change for one of its modules.
The module sys
implements some standardized debug and trace facilities. The Deb
information passed with the handle_system_msg
function can be manipulated, created and inspected using the following functions:
sys:debug_options([Opt]) -> Deb
. This function creates the Deb
information. Opt
is one of
trace | log | statistics | {log_to_file, FileName} | {install, {Func, FuncState}}
.
sys:get_debug(Opt, Deb, Default) -> Value
. This function fetches the current value of Opt
in Deb
. Default
is any term which describes the default value.
sys:handle_debug(Deb, FormFunc, Info, Event) -> Deb
. This function is called whenever we want to handle an event as a debug event. It has the following arguments:
FormFunc
is one of {Module, Function}
or a fun with arity 3. This function is called as FormFunc(Dev,Event,Info)
, where Dev
is used in the same manner as in io:format(Dev,Format,Args)
. The FormFunc
function is used to display the events.
Info
is any term passed to FormFunc
.
Event
is {in, Msg} | {in, Msg, From} | {out, Msg, To} | term()
The following functions in the module sys
can be used to activate or de-activate debugging, or to install your own trigger/debug functions: log/2
, log/3
, trace/2
trace/3
, statistics/2
, statistics/3
,
log_to_file/2
, log_to_file/3
, no_debug/1
,
no_debug/2
, install/2
, install/3
, remove/2
, remove/3
.
Refer to the Reference Manual, stdlib
, module sys
for details.
The following example of a simple server illustrates how to use the special processes described.
-module(test). -copyright('Copyright (c) 1991-97 Ericsson Telecom AB'). -vsn('$Revision: /main/release/2 $'). -export([start/1, init/2, system_continue/3, system_terminate/4, write_debug/3]). start(Options) -> case whereis(test_server) of undefined -> Pid = proc_lib:spawn_link(test, init, [self(), Options]), register(test_server, Pid), {ok, Pid}; Pid -> {error, {already_started, Pid}} end. init(Parent, Options) -> process_flag(trap_exit, true), Deb = sys:debug_options(Options), loop([], Parent, Deb). loop(State, Parent, Deb) -> receive {system, From, Request} -> sys:handle_system_msg(Request, From, Parent, test, Deb, State); {'EXIT', Parent, Reason} -> cleanup(State), exit(Reason); {From, OurMsgs} -> NewDeb = sys:handle_debug(Deb, {test, write_debug}, test_server, {in, OurMsgs, From}), {Answer, NewState} = do_something(OurMsgs, State), From ! {self(),Answer}, NewerDeb = sys:handle_debug(NewDeb, {test, write_debug}, test_server, {out, {self(), Answer}, From}), loop(NewState, Parent, NewerDeb); What -> NewDeb = sys:handle_debug(Deb, {test, write_debug}, test_server, {in, What}), loop(State, Parent, NewDeb) end. cleanup(State) -> ok. do_something(Msg, State) -> %% Here we shall perform actions to handle the request. {ok, State}. %% Here are the sys call back functions system_continue(Parent, Deb, State) -> loop(State, Parent, Deb). system_terminate(Reason, Parent, Deb, State) -> cleanup(State), exit(Reason). write_debug(Dev, Event, Name) -> io:format(Dev, "~p event = ~p~n", [Name, Event]).
This can be used as follows:
1> test:start([trace]). {ok,<0.21.0>} 2> test_server ! hej. test_server event = {in,hej} hej 3> test_server ! {self(), hopp}. test_server event = {in,hopp,<0.18.0>} {<0.18.0>,hopp} test_server event = {out,{<0.21.0>,ok},<0.18.0>} 4> receive X -> X end. {<0.21.0>,ok} 5> sys:trace(test_server, false). ok 6> sys:log(test_server, true). ok 7> test_server ! message_1. message_1 8> test_server ! message_2. message_2 9> sys:log(test_server, print). test_server event = {in,message_1} test_server event = {in,message_2} ok