[Erlang Systems]

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:

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:

Note!

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:

Note!

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:

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-2001 Ericsson Utvecklings AB