7 Special Processes
This section describes how to write a process which understands and behaves like the generic processes, for example
gen_serverandgen_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_libmodule 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 thannormalorshutdown.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_linkorspawn_linkfunctions 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:
Requestis any term
Fromis the process identity of the calling process
Parentis the parent process identity
Modis the current module
Debis a list of debug information
Miscis any term describing the internal state.
The
handle_system_msg/6function never returns. It calls one of the functionssystem_continue/3orsystem_terminate/4to return to the original module. These functions are described in the following list.The
Modmodule must export the following functions, which may be called from thesys:handle_system_msg/6function:
system_continue(Parent, Deb, Misc), whereMiscis 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). TheParentprocess 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.Extragives extra information about the code change.OldVsnis the old version ofModule. Thesystem_code_changefunction 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 fromParentis an order to terminate. Normally one shall terminate the process with the sameReasonasParent.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_eventprocesses, which add new handlers at runtime.This message is
{get_modules, From}. The reply to this message is{modules, Modules}, whereModulesis 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
sysimplements some standardized debug and trace facilities. TheDebinformation passed with thehandle_system_msgfunction can be manipulated, created and inspected using the following functions:
sys:debug_options([Opt]) -> Deb. This function creates theDebinformation.Optis one oftrace | log | statistics | {log_to_file, FileName} | {install, {Func, FuncState}}.
sys:get_debug(Opt, Deb, Default) -> Value. This function fetches the current value ofOptinDeb.Defaultis 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:
FormFuncis one of{Module, Function}or a fun with arity 3. This function is called asFormFunc(Dev,Event,Info), whereDevis used in the same manner as inio:format(Dev,Format,Args). TheFormFuncfunction is used to display the events.
Infois any term passed toFormFunc.
Eventis{in, Msg} | {in, Msg, From} | {out, Msg, To} | term()
The following functions in the module
syscan be used to activate or de-activate debugging, or to install your own trigger/debug functions:log/2,log/3,trace/2trace/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, modulesysfor 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