6 Sys and Proc_Lib
The module
sys
contains functions for simple debugging of processes implemented using behaviours. There are also functions that, together with functions in the moduleproc_lib
, can be used to implement a special process, a process which comply to the OTP design principles without making use of a behaviour.6.1 Simple Debugging
The module
sys
contains some functions for simple debugging of processes implemented using behaviours. We use thecode_lock
example from the gen_event chapter to illustrate this:% erl Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] Eshell V5.2.3.6 (abort with ^G) 1> code_lock:start_link([1,2,3,4]). {ok,<0.32.0>} 2> sys:statistics(code_lock, true). ok 3> sys:trace(code_lock, true). ok 4> code_lock:button(4). *DBG* code_lock got event {button,4} in state closed ok *DBG* code_lock switched to state closed 5> code_lock:button(3). *DBG* code_lock got event {button,3} in state closed ok *DBG* code_lock switched to state closed 6> code_lock:button(2). *DBG* code_lock got event {button,2} in state closed ok *DBG* code_lock switched to state closed 7> code_lock:button(1). *DBG* code_lock got event {button,1} in state closed ok OPEN DOOR *DBG* code_lock switched to state open *DBG* code_lock got event timeout in state open CLOSE DOOR *DBG* code_lock switched to state closed 8> sys:statistics(code_lock, get). {ok,[{start_time,{{2003,6,12},{14,11,40}}}, {current_time,{{2003,6,12},{14,12,14}}}, {reductions,333}, {messages_in,5}, {messages_out,0}]} 9> sys:statistics(code_lock, false). ok 10> sys:trace(code_lock, false). ok 11> sys:get_status(code_lock). {status,<0.32.0>, {module,gen_fsm}, [[{'$ancestors',[<0.30.0>]}, {'$initial_call',{gen,init_it, [gen_fsm, <0.30.0>, <0.30.0>, {local,code_lock}, code_lock, [1,2,3,4], []]}}], running, <0.30.0>, [], [code_lock,closed,{[],[1,2,3,4]},code_lock,infinity]]}6.2 Special Processes
This section describes how to write a process which comply to the OTP design principles, without making use of a behaviour. Such a process should:
- be started in a way that makes the process fit into a supervision tree,
- support the
sys
debug facilities, and
- take care of system messages.
System messages are messages with special meaning, used in the supervision tree. Typical system messages are requests for trace output, and requests to suspend or resume process execution (used during release handling). Processes implemented using behaviours automatically understand these messages.
6.2.1 Example
The simple server from the Overview chapter, implemented using
sys
andproc_lib
so it fits into a supervision tree:-module(ch4). -export([start_link/0]). -export([alloc/0, free/1]). -export([init/1]). -export([system_continue/3, system_terminate/4, write_debug/3]). start_link() -> proc_lib:start_link(ch4, init, [self()]). alloc() -> ch4 ! {self(), alloc}, receive {ch4, Res} -> Res end. free(Ch) -> ch4 ! {free, Ch}, ok. init(Parent) -> register(ch4, self()), Chs = channels(), Deb = sys:debug_options([]), proc_lib:init_ack(Parent, {ok, self()}), loop(Chs, Parent, Deb). loop(Chs, Parent, Deb) -> receive {From, alloc} -> Deb2 = sys:handle_debug(Deb, {ch4, write_debug}, ch4, {in, alloc, From}), {Ch, Chs2} = alloc(Chs), From ! {ch4, Ch}, Deb3 = sys:handle_debug(Deb2, {ch4, write_debug}, ch4, {out, {ch4, Ch}, From}), loop(Chs2, Parent, Deb3); {free, Ch} -> Deb2 = sys:handle_debug(Deb, {ch4, write_debug}, ch4, {in, {free, Ch}}), Chs2 = free(Ch, Chs), loop(Chs2, Parent, Deb2); {system, From, Request} -> sys:handle_system_msg(Request, From, Parent, ch4, Deb, Chs) end. system_continue(Parent, Deb, Chs) -> loop(Chs, Parent, Deb). system_terminate(Reason, Parent, Deb, Chs) -> exit(Reason). write_debug(Dev, Event, Name) -> io:format(Dev, "~p event = ~p~n", [Name, Event]).Example on how the simple debugging functions in
sys
can be used forch4
as well:% erl Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] Eshell V5.2.3.6 (abort with ^G) 1> ch4:start_link(). {ok,<0.30.0>} 2> sys:statistics(ch4, true). ok 3> sys:trace(ch4, true). ok 4> ch4:alloc(). ch4 event = {in,alloc,<0.25.0>} ch4 event = {out,{ch4,ch1},<0.25.0>} ch1 5> ch4:free(ch1). ch4 event = {in,{free,ch1}} ok 6> sys:statistics(ch4, get). {ok,[{start_time,{{2003,6,13},{9,47,5}}}, {current_time,{{2003,6,13},{9,47,56}}}, {reductions,109}, {messages_in,2}, {messages_out,1}]} 7> sys:statistics(ch4, false). ok 8> sys:trace(ch4, false). ok 9> sys:get_status(ch4). {status,<0.30.0>, {module,ch4}, [[{'$ancestors',[<0.25.0>]},{'$initial_call',{ch4,init,[<0.25.0>]}}], running, <0.25.0>, [], [ch1,ch2,ch3]]}6.2.2 Starting the Process
A function in the
proc_lib
module should be used to start the process. There are several possible functions, for examplespawn_link/3,4
for asynchronous start andstart_link/3,4,5
for synchronous start.A process started using one of these functions will store some information, for example about the ancestors and initial call, that is needed for a process in a supervision tree.
Also, if the process terminates with another reason than
normal
orshutdown
, a crash report (see SASL User's Guide) is generated.In the example, synchronous start is used. The process is started by calling
ch4:start_link()
start_link() -> proc_lib:start_link(ch4, init, [self()]).
start_link
calls the functionproc_lib:start_link
. This functions spawns and links to a new process, executingch4:init(Pid)
, wherePid
is the pid of process callingch4:start_link()
(the parent process).In
init
, all initialization including name registration is done. The process must also acknowledge that it has been started to the parent:init(Parent) -> ... proc_lib:init_ack(Parent, {ok, self()}), loop(...).
proc_lib:start_link
is synchronous and does not return untilproc_lib:init_ack
has been called.6.2.3 Debugging
To support the debug facilites in
sys
, a termDeb
is initialized ininit
usingsys:debug_options/1
:init(Parent) -> ... Deb = sys:debug_options([]), ... loop(Chs, Parent, Deb).
sys:debug_options/1
takes a list of options as argument. Here the list is empty, which means no debugging is enabled initially. Seesys(3)
for information about possible options.Then for each system event, the following function should be called:
sys:handle_debug(Deb, Func, Info, Event)System events are:
- Incoming messages, represented as
{in, Msg}
or{in, Msg, From}
.
- Outgoing messages, represented as
{out, Msg, To}
.
- The user may define own system events, represented by an arbitrary term.
Func
is a tuple{Module, Name}
(or a fun) and should be a user defined function used to format trace output. The function is called asModule:Name(Dev, Event, Info)
.In the example,
handle_debug
is called for each incoming and outgoing message. The format functionFunc
is the functionch4:write_debug/3
which prints the message usingio:format/3
.loop(Chs, Parent, Deb) -> receive {From, alloc} -> Deb2 = sys:handle_debug(Deb, {ch4, write_debug}, ch4, {in, alloc, From}), {Ch, Chs2} = alloc(Chs), From ! {ch4, Ch}, Deb3 = sys:handle_debug(Deb2, {ch4, write_debug}, ch4, {out, {ch4, Ch}, From}), loop(Chs2, Parent, Deb3); {free, Ch} -> Deb2 = sys:handle_debug(Deb, {ch4, write_debug}, ch4, {in, {free, Ch}}), Chs2 = free(Ch, Chs), loop(Chs2, Parent, Deb2); ... end. write_debug(Dev, Event, Name) -> io:format(Dev, "~p event = ~p~n", [Name, Event]).6.2.4 Handling System Messages
System messages are received as:
{system, From, Request}The content and meaning of these messages do not need to be interpreted by the process. Instead the following function should be called:
sys:handle_system_msg(Request, From, Parent, Module, Deb, State)This function does not return. It will handle the system message and then call:
Module:system_continue(Parent, Deb, State)if process execution should continue, or:
Module:system_terminate(Reason, Parent, Deb, State)if the process should terminate. Note that a process in a supervision tree is expected to terminate with the same reason as its parent.
Request
andFrom
should be passed as-is from the system message to the call tohandle_system_msg
.
Parent
is the pid of the parent.
Module
is the name of the module.
Deb
is the debug information.
State
is a term describing the internal state and is passed tosystem_continue
/system_terminate
.
In the example:
loop(Chs, Parent, Deb) -> receive ... {system, From, Request} -> sys:handle_system_msg(Request, From, Parent, ch4, Deb, Chs) end. system_continue(Parent, Deb, Chs) -> loop(Chs, Parent, Deb). system_terminate(Reason, Parent, Deb, Chs) -> exit(Reason).If the special process is set to trap exits, note that if the parent process terminates, the expected behaviour is to terminate with the same reason:
init(...) -> ..., process_flag(trap_exit, true), ..., loop(...). loop(...) -> receive ... {exit, Parent, Reason} -> ..maybe some cleaning up here.. exit(Reason); ... end.