From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- system/doc/design_principles/spec_proc.xml | 460 +++++++++++++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100644 system/doc/design_principles/spec_proc.xml (limited to 'system/doc/design_principles/spec_proc.xml') diff --git a/system/doc/design_principles/spec_proc.xml b/system/doc/design_principles/spec_proc.xml new file mode 100644 index 0000000000..f0f62891b6 --- /dev/null +++ b/system/doc/design_principles/spec_proc.xml @@ -0,0 +1,460 @@ + + + + +
+ + 19972009 + Ericsson AB. All Rights Reserved. + + + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + + + Sys and Proc_Lib + + + + + spec_proc.xml +
+

The module sys contains functions for simple debugging of + processes implemented using behaviours.

+

There are also functions that, together with functions in + the module proc_lib, can be used to implement a + special process, a process which comply to the OTP design + principles without making use of a standard behaviour. They can + also be used to implement user defined (non-standard) behaviours.

+

Both sys and proc_lib belong to the STDLIB + application.

+ +
+ Simple Debugging +

The module sys contains some functions for simple debugging + of processes implemented using behaviours. We use the + code_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]]}
+
+ +
+ Special Processes +

This section describes how to write a process which comply to + the OTP design principles, without making use of a standard + behaviour. Such a process should:

+ + be started in a way that makes the process fit into a + supervision tree, + support the sysdebug 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 + standard behaviours automatically understand these messages.

+ +
+ Example +

The simple server from + the Overview chapter, + implemented using sys and proc_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 for ch4 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]]}
+
+ +
+ Starting the Process +

A function in the proc_lib module should be used to + start the process. There are several possible functions, for + example spawn_link/3,4 for asynchronous start and + start_link/3,4,5 for synchronous start.

+

A process started using one of these functions will store + information that is needed for a process in a supervision tree, + for example about the ancestors and initial call.

+

Also, if the process terminates with another reason than + normal or shutdown, 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()]). +

ch4:start_link calls the function + proc_lib:start_link. This function takes a module name, + a function name and an argument list as arguments and spawns + and links to a new process. The new process starts by executing + the given function, in this case ch4:init(Pid), where + Pid is the pid (self()) of the first process, that + is the parent process.

+

In init, all initialization including name registration + is done. The new 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 + until proc_lib:init_ack has been called.

+
+ +
+ + Debugging +

To support the debug facilites in sys, we need a + debug structure, a term Deb which is + initialized using sys: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. See sys(3) for information about possible + options.

+

Then for each system event that we want to be logged + or traced, the following function should be called.

+ +sys:handle_debug(Deb, Func, Info, Event) => Deb1 + + +

Deb is the debug structure.

+
+ +

Func is a tuple {Module, Name} (or a fun) and + should specify a (user defined) function used to format + trace output. For each system event, the format function is + called as Module:Name(Dev, Event, Info), where:

+ + +

Dev is the IO device to which the output should + be printed. See io(3).

+
+ +

Event and Info are passed as-is from + handle_debug.

+
+
+
+ +

Info is used to pass additional information to + Func, it can be any term and is passed as-is.

+
+ +

Event is the system event. It is up to the user to + define what a system event is and how it should be + represented, but typically at least incoming and outgoing + messages are considered system events and represented by + the tuples {in,Msg[,From]} and {out,Msg,To}, + respectively.

+
+
+

handle_debug returns an updated debug structure + Deb1.

+

In the example, handle_debug is called for each incoming + and outgoing message. The format function Func is + the function ch4:write_debug/3 which prints the message + using io: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]). +
+ +
+ + 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 and From should be passed as-is from + the system message to the call to handle_system_msg. + Parent is the pid of the parent. + Module is the name of the module. + Deb is the debug structure. + State is a term describing the internal state and + is passed to system_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 behavior 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. +
+
+ +
+ User-Defined Behaviours +

To implement a user-defined behaviour, write code similar to + code for a special process but calling functions in a callback + module for handling specific tasks.

+

If it is desired that the compiler should warn for missing + callback functions, as it does for the OTP behaviours, implement + and export the function:

+ +behaviour_info(callbacks) -> + [{Name1,Arity1},...,{NameN,ArityN}]. +

where each {Name,Arity} specifies the name and arity of + a callback function.

+

When the compiler encounters the module attribute + -behaviour(Behaviour). in a module Mod, it will call + Behaviour:behaviour_info(callbacks) and compare the result + with the set of functions actually exported from Mod, and + issue a warning if any callback function is missing.

+

Example:

+ +%% User-defined behaviour module +-module(simple_server). +-export([start_link/2,...]). +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [{init,1}, + {handle_req,1}, + {terminate,0}]. + +start_link(Name, Module) -> + proc_lib:start_link(?MODULE, init, [self(), Name, Module]). + +init(Parent, Name, Module) -> + register(Name, self()), + ..., + Dbg = sys:debug_options([]), + proc_lib:init_ack(Parent, {ok, self()}), + loop(Parent, Module, Deb, ...). + +... +

In a callback module:

+ +-module(db). +-behaviour(simple_server). + +-export([init/0, handle_req/1, terminate/0]). + +... +
+
+ -- cgit v1.2.3