> ## 7 Special Processes

> 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:

> - 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 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.

> ### 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 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 `handle_system_msg/6` function never returns. It calls one of the functions `system_continue/3` or `system_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 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 `-vsn(Vsn).` in the Erlang source code.

> 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`.

> ### 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}`, 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.

> ### 7.4 Debugging

> 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.

> #### 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

Copyright © 1991-2003 Ericsson Utvecklings AB