Erlang: supervisor(3), adding a child process
Asked Answered
C

2

14

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

Camelback answered 29/1, 2011 at 13:48 Comment(0)
C
20

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:

Coulee answered 29/1, 2011 at 16:3 Comment(6)
Thank you very much. And wher my_start_child/0?Camelback
@shk: Oh, it's 2:22 AM here, just forgot to remore this export :-) This function contained supervisor:start_child(ch_sup, []). I'll edit the post. One important thing to mention: your child should always link to the supervisor, that's why I used gen_server:start_link in child start function, not just gen_server:start; otherwise supervisor would have no way to know if its children processes are still running or terminated (normally or abnormally).Coulee
Where is "ch_sup" ? I think it should be "root_sup"Verrazano
You started the child process from the erlang shell. How to start it automatically right after the supervisor starts ? Is there a placeholder in the supervisor code you can add supervisor:start_child(ch_sup, []). ?Collogue
This is bad practice, you shouldn't do the unlinkPreconscious
Correct. This call did more harm than good. No idea why I haven't noticed it right away. Fixed now. Thanks for reporting!Coulee
E
7

In Supervisor Behaviour section of the OTP Design Principles part of the Erlang docs there is an example of how to use simple_one_for_one and dynamic children. I recommend the whole Design Principles part as it provides much insight into how OTP works.

Excreta answered 29/1, 2011 at 15:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.