7 Special Processes
This section describes how to write a process which understands and behaves like the generic processes, for example
gen_server
andgen_fsm
. In this context, behaves means:
- the process takes care of special system messages
- it creates a crash report if terminated abnormally.
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.
7.1 Starting a Process
The
proc_lib
module should be used to start a process. This process wraps the initial function call with acatch
, and a crash report is generated if the process terminates with another reason thannormal
orshutdown
.The function which starts a new process shall always return
{ok,Pid}
when successfully started, or{error,Reason}
in case of failure. One of theproc_lib:start_link
orspawn_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.7.2 System Messages
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 functionsys: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 termFrom
is the process identity of the calling processParent
is the parent process identityMod
is the current moduleDeb
is a list of debug informationMisc
is any term describing the internal state.
The
handle_system_msg/6
function never returns. It calls one of the functionssystem_continue/3
orsystem_terminate/4
to return to the original module. These functions are described in the following list.The
Mod
module must export the following functions, which may be called from thesys:handle_system_msg/6
function:
system_continue(Parent, Deb, Misc)
, whereMisc
is the internal state (i.e. loop data) forwarded in the above call tohandle_system_msg/6
. This function resumes normal execution. .system_terminate(Reason, Parent, Deb, Misc)
. TheParent
process has terminated withReason
, 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 ofModule
. Thesystem_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
-vsn(Vsn).
in the Erlang source code.According to the shutdown protocol, a
{'EXIT',Parent,Reason}
message fromParent
is an order to terminate. Normally one shall terminate the process with the sameReason
asParent
.7.3 Other Messages
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}
, whereModules
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.
7.4 Debugging
The module
sys
implements some standardized debug and trace facilities. TheDeb
information passed with thehandle_system_msg
function can be manipulated, created and inspected using the following functions:
sys:debug_options([Opt]) -> Deb
. This function creates theDeb
information.Opt
is one oftrace | log | statistics | {log_to_file, FileName} | {install, {Func, FuncState}}
.sys:get_debug(Opt, Deb, Default) -> Value
. This function fetches the current value ofOpt
inDeb
.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 asFormFunc(Dev,Event,Info)
, whereDev
is used in the same manner as inio:format(Dev,Format,Args)
. TheFormFunc
function is used to display the events.Info
is any term passed toFormFunc
.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
, modulesys
for details.7.4.1 An Example
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