Erlang: supervisor(3), adding a child process

0xAX picture 0xAX · Jan 29, 2011 · Viewed 7.4k times · Source

Where can I find example on how to add dynamic child processes to an existing supervisor (simple_one_for_one restart strategy) ?

Answer

Yasir Arsanukaev picture Yasir Arsanukaev · Jan 29, 2011

I did some research, and below is what I have.

First, this is a sample callback module of a supervisor:

-module(root_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).

start_link() ->
     {ok, Pid} = supervisor:start_link({local, ?MODULE}, 
          ?MODULE, []),
     {ok, Pid}.

init(_Args) ->
     RestartStrategy = {simple_one_for_one, 10, 60},
     ChildSpec = {ch1, {ch1, start_link, []},
          permanent, brutal_kill, worker, [ch1]},
     Children = [ChildSpec],
     {ok, {RestartStrategy, Children}}.

And this is a callback module of a child which will be added to the suprervision tree dynamically:

-module(ch1).

-behaviour(gen_server).

% Callback functions which should be exported
-export([init/1]).
-export([handle_cast/2]).

% user-defined interface functions
-export([start_link/0]).

start_link() ->
     gen_server:start_link(?MODULE, [], []).

init(_Args) ->
     io:format("ch1 has started (~w)~n", [self()]),
     % If the initialization is successful, the function
     % should return {ok,State}, {ok,State,Timeout} ..
     {ok, ch1State}.

handle_cast(calc, State) ->
     io:format("result 2+2=4~n"),
     {noreply, State};
handle_cast(calcbad, State) ->
     io:format("result 1/0~n"),
     1 / 0,
     {noreply, State}.

This is how we usually start the supervisor:

1> ch_sup:start_link().
{ok,<0.33.0>}

Now let's start our first child process:

2> {ok, Child1Pid} = supervisor:start_child(ch_sup, []).
ch1 has started (<0.35.0>)
{ok,<0.35.0>}

You can dynamically start child processes; let's start another child:

3> {ok, Child2Pid} = supervisor:start_child(ch_sup, []).
ch1 has started (<0.37.0>)
{ok,<0.37.0>}

You may see that our processes did start (note the last two):

4> erlang:processes().
[<0.0.0>,<0.2.0>,<0.4.0>,<0.5.0>,<0.7.0>,<0.8.0>,<0.9.0>,
 <0.10.0>,<0.11.0>,<0.12.0>,<0.13.0>,<0.14.0>,<0.15.0>,
 <0.16.0>,<0.17.0>,<0.18.0>,<0.19.0>,<0.20.0>,<0.21.0>,
 <0.22.0>,<0.23.0>,<0.24.0>,<0.25.0>,<0.26.0>,<0.27.0>,
 <0.31.0>,<0.33.0>,<0.35.0>,<0.37.0>]

Now let's make our first child process do something:

5> gen_server:cast(Child1Pid, calc).
result 2+2=4
ok

So far, so good. Now we'll make our first child to evaluate some bad code:

6> gen_server:cast(Child1Pid, calcbad).
result 1/0
ok    
7> 
=ERROR REPORT==== 10-Feb-2011::01:32:15 ===
** Generic server <0.35.0> terminating 
** Last message in was {'$gen_cast',calcbad}
** When Server state == ch1State
** Reason for termination == 
** {'function not exported',
       [{ch1,terminate,
            [{badarith,
                 [{ch1,handle_cast,2},
                  {gen_server,handle_msg,5},
                  {proc_lib,init_p_do_apply,3}]},
             ch1State]},
        {gen_server,terminate,6},
        {proc_lib,init_p_do_apply,3}]}
ch1 has started (<0.42.0>)
7>

In the report, you may see that the division by zero caused an exception and the process was terminated. But the supervisor takes care of it and immediately starts another child process (note the last line).

We can check to make sure that the other child process we started previously is still alive (note <0.37.0>):

7> erlang:processes().                 
[<0.0.0>,<0.2.0>,<0.4.0>,<0.5.0>,<0.7.0>,<0.8.0>,<0.9.0>,
 <0.10.0>,<0.11.0>,<0.12.0>,<0.13.0>,<0.14.0>,<0.15.0>,
 <0.16.0>,<0.17.0>,<0.18.0>,<0.19.0>,<0.20.0>,<0.21.0>,
 <0.22.0>,<0.23.0>,<0.24.0>,<0.25.0>,<0.26.0>,<0.27.0>,
 <0.31.0>,<0.33.0>,<0.37.0>,<0.42.0>]
8>

We can even make it do something for us:

8> gen_server:cast(Child2Pid, calc).   
result 2+2=4
9>

The following are the Erlang manual pages you'll want to read: