## 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-2002 Ericsson Utvecklings AB