Erlang supervisor does not restart child

780 views Asked by At

I'm trying to learn about erlang supervisors. I have a simple printer process that prints hello every 3 seconds. I also have a supervisor that must restart the printer process if any exception occurs.

Here is my code:

test.erl:

-module(test).
-export([start_link/0]).    

start_link() ->
    io:format("started~n"),
    Pid = spawn_link(fun() -> loop() end),
    {ok, Pid}.    

loop() ->
    timer:sleep(3000),
    io:format("hello~n"),
    loop().

test_sup.erl:

-module(test_sup).
-behaviour(supervisor).

-export([start_link/0]).
-export([init/1]).

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

init(_Args) ->
    SupFlags = #{strategy => one_for_one, intensity => 1, period => 5},    
    ChildSpecs = [#{id => test,
                    start => {test, start_link, []},
                    restart => permanent,
                    shutdown => brutal_kill,
                    type => worker,
                    modules => [test]}],    
    {ok, {SupFlags, ChildSpecs}}.

Now I run this program and start the supervisor using test_sup:start_link(). command and after a few seconds, I raise an exception. Why the supervisor does not restart the printer process?

Here is the shell output:

1> test_sup:start_link().
started
{ok,<0.36.0>}
hello
hello
hello
hello          
2> erlang:error(err).

=ERROR REPORT==== 13-Dec-2016::00:57:10 ===
** Generic server test_sup terminating 
** Last message in was {'EXIT',<0.34.0>,
                           {err,
                               [{erl_eval,do_apply,6,
                                    [{file,"erl_eval.erl"},{line,674}]},
                                {shell,exprs,7,
                                    [{file,"shell.erl"},{line,686}]},
                                {shell,eval_exprs,7,
                                    [{file,"shell.erl"},{line,641}]},
                                {shell,eval_loop,3,
                                    [{file,"shell.erl"},{line,626}]}]}}
** When Server state == {state,
                            {local,test_sup},
                            one_for_one,
                            [{child,<0.37.0>,test,
                                 {test,start_link,[]},
                                 permanent,brutal_kill,worker,
                                 [test]}],
                            undefined,1,5,[],0,test_sup,[]}
** Reason for termination == 
** {err,[{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,674}]},
         {shell,exprs,7,[{file,"shell.erl"},{line,686}]},
         {shell,eval_exprs,7,[{file,"shell.erl"},{line,641}]},
         {shell,eval_loop,3,[{file,"shell.erl"},{line,626}]}]}
** exception error: err
2

There are 2 answers

0
Pascal On BEST ANSWER

When you execute erlang:error(err)., you are killing the calling process, your shell.

As you have used start_link to start the supervisor, it is also killed, and the loop also.

The shell is automatically restarted (thanks to some supervisor), but nobody restart your test supervisor, which cannot restart the loop.

To make this test you should do:

in module test:

start_link() ->
    Pid = spawn_link(fun() -> loop() end),
    io:format("started ~p~n",[Pid]),
    {ok, Pid}.

you will get a prompt:

started <0,xx,0>

where <0,xx,0> is the loop pid, and in the shell you can call

exit(pid(0,xx,0), err).

to kill the loop only.

0
Philip On

Here's the architecture you've created with your files:

test_sup (supervisor)
  ^
  |
  v
 test (worker)

Then you start your supervisor by calling start_link() in the shell. This creates another bidirectional link:

shell
  ^
  |
  v
test_sup (supervisor)
  ^
  |
  v
 test (worker)

With a bidirectional link, if either side dies, the other side is killed.

When you run erlang:error, you're causing an error in your shell!

Your shell is linked to your supervisor, so Erlang kills the supervisor in response. By chain reaction, your worker gets killed too.

I think you intended to send the error condition to your worker rather than the shell:

  1. Determine the Pid of your worker: supervisor:which_children
  2. Call erlang:exit(Pid, Reason) on the worker's Pid.