I'm very new to OTP, I'm trying to create simple example to understand supervisor behaviour:
Here is simple increment server
-module( inc_serv ).
-behaviour( gen_server ).
-export( [ start/0, inc/1, stop/0 ] ).
-export( [ init/1, handle_call/3, terminate/2 ] ).
start() ->
gen_server:start_link( { local, ?MODULE }, ?MODULE, no_args, [] ).
stop() ->
gen_server:call( ?MODULE, stop ).
inc( Num ) ->
gen_server:call( ?MODULE, { num, Num } ).
init( no_args ) ->
io:format( "~p~n", [ "Increment server started :)" ] ),
{ ok, no_state }.
handle_call( { num, Num }, _From, no_state ) ->
{ reply, Num + 1, no_state };
handle_call( stop, _From, no_state ) ->
{ stop, normal, ok, no_state }.
terminate( Reason, no_state ) ->
io:format( "~p~n", [ "Increment server stopped" ] ).
And I'd like to make it supervised by this module:
-module( supervisor_inc ).
-behaviour( supervisor ).
-export( [ start/0 ] ).
-export( [ init/1 ] ).
start() ->
supervisor:start_link( { local, ?MODULE }, ?MODULE, no_args ).
init( no_args ) ->
process_flag( trap_exit, true ),
Supervisor_Spec = { one_for_one, 1, 1 },
IncServ_Spec = {
inc_serv,
{ inc_serv, start, [] },
permanent, 2000, worker, [ inc_serv ] },
{ ok, { Supervisor_Spec, [ IncServ_Spec ] } }.
After that I've performed in erlang shell next steps:
1>
1> c(inc_serv).
{ok,inc_serv}
2>
2> c(supervisor_inc).
{ok,supervisor_inc}
3>
3> supervisor_inc:start().
"Increment server started :)"
{ok,<0.43.0>}
4>
4> inc_serv:inc( 7 ).
8
5> inc_serv:inc( 8 ).
9
After this I've tried next (as I expected I've got error):
6> inc_serv:inc( bad_arg ).
"Increment server stopped"
"Increment server started :)"
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server supervisor_inc terminating
** Last message in was {'EXIT',<0.31.0>,
{{{badarith,
[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,
[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,
[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,
[{file,"shell.erl"},{line,608}]}]}}
** When Server state == {state,
{local,supervisor_inc},
one_for_one,
[{child,<0.48.0>,inc_serv,
{inc_serv,start,[]},
permanent,2000,worker,
[inc_serv]}],
undefined,1,1,
[{1345,739526,107495}],
supervisor_inc,no_args}
** Reason for termination ==
** {{{badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,608}]}]}
** exception exit: {{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
After this I've expected - my supervisor to restart inc_serv. But it didn't:
7> inc_serv:inc( 8 ).
** exception exit: {noproc,{gen_server,call,[inc_serv,{num,8}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
Could you help me to understand what happened? And how should I rewrite my supervisor, to make it able to restart inc_serv
Thanks
This is actually a kind of race condition.
As you might know, the Erlang shell itself is a normal Erlang process. When you start your supervisor from the shell, the supervisor is linked to the shell (because you use supervisor:start_link/3).
When you call your gen_server process, that process crashes (and is correctly restarted by the supervisor, as you can see by the subsequent "Increment server started :)" output).
However, at the same time, your call to gen_server:call/2 will result in the same crash (a gen_server crashing during the call will emit the same crash through the gen_server:call/2 function). This then crashes the shell process, which is linked to your supervisor, which in turn crashes with the same reason (badarith).
Basically, your supervisor is backstabbed by your shell process, after it loyally restarted your gen_server. Like so:
+---------(6)exit----------+ +---------(5)restart---------+
| | | |
| v | v
Shell ---(1)start_link---> supervisor ---(2)start_link---> gen_server
| ^ ^ | ^ | ^
| | | | | | |
| | | +---------(7)exit---------+ | |
| | | | |
| +-------------------------+--------------(4)exit------------+ |
| |
+---------------------------(3)call--------------------------------+
You can avoid this by calling catch inc_serv:inc(bad_arg). in your shell:
90> inc_serv:inc(7).
8
91> catch inc_serv:inc(bad_arg).
"Increment server stopped"
=ERROR REPORT==== 23-Aug-2012::22:10:02 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
"Increment server started :)"
{'EXIT',{{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}}
92> inc_serv:inc(7).
8
Related
Given the following code:
-module(appender_server).
-behaviour(gen_server).
-export([start_link/1, stop/0]).
-export([append_synched/2]).
-export([init/1, handle_call/3, handle_cast/2]).
start_link(FilePath) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, FilePath, []).
init(FilePath) ->
{ok, TheFile} = file:open(FilePath, [append]), % File is created if it does not exist.
io_lib:print(TheFile).
append_synched(FilePath, Text) ->
gen_server:call(?MODULE, {append_synched, FilePath, Text}).
handle_call({append_synched, FilePath, Text}, _From, LoopData) ->
io:fwrite("Hello!~n", []),
io:fwrite("LoopData:~p~n", LoopData),
io:fwrite("~p~n", FilePath),
io:fwrite("~p~n", Text).
handle_cast(append_asynched, Str) ->
{ok, theFile} = file:write_file(theFile, Str).
stop() ->
gen_server:cast(?MODULE, stop).
I then use the erlang shell to call the following and the get the errors:
1> cd("c:/Users/TalTe/WebAppsProjects/fileappender/src/").
c:/Users/TalTe/WebAppsProjects/fileappender/src
ok
2> c(appender_server).
{ok,appender_server}
3> appender_server:start_link("c:/temp/file.txt").
{error,{bad_return_value,"<0.88.0>"}}
4> =CRASH REPORT==== 2-Dec-2020::23:30:19.687000 ===
crasher:
initial call: appender_server:init/1
pid: <0.87.0>
registered_name: appender_server
exception exit: {bad_return_value,"<0.88.0>"}
in function gen_server:init_it/6 (gen_server.erl, line 409)
ancestors: [<0.79.0>]
message_queue_len: 0
messages: []
links: [<0.79.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 376
stack_size: 28
reductions: 257
neighbours:
neighbour:
pid: <0.79.0>
registered_name: []
initial_call: {erlang,apply,2}
current_function: {io,execute_request,2}
ancestors: []
message_queue_len: 0
links: [<0.78.0>,<0.87.0>]
trap_exit: false
status: waiting
heap_size: 1598
stack_size: 26
reductions: 5365
current_stacktrace: [{io,execute_request,2,[{file,"io.erl"},{line,571}]},
{shell,exprs,7,[{file,"shell.erl"},{line,693}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,642}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,627}]}]
** exception error: {bad_return_value,"<0.88.0>"}
4> appender_server:append_synched("c:/temp:/file.txt", "Hello").
** exception exit: {noproc,{gen_server,call,
[appender_server,
{append_synched,"c:/temp:/file.txt","Hello"}]}}
in function gen_server:call/2 (gen_server.erl, line 238)
5>
Please help me understand what am I doing wrong here?
BTW, the file was created despite the error on step 3.
The init callback should return {ok, SomeState}. In your case the return value of init is the return value of io_lib:print()
Hello i am trying to run a supervisor whose workers are not gen_server(s).
My supervisor is defined in the same module as the worker for brevity:
I keep getting this error and i have tried putting the MFA attribute in [ ] to no avail.I have also put the ChildSpec in [ ].
What am i missing ?
I do not want my supervisor to have any workers when starting it.
Error
> X=sup:start_link().
> ** exception error: no match of right hand side value {error,
> {bad_start_spec,[]}}
> in function sup:start_link/0 (c:/Erlang/ProcessPool/sup.erl, line 6)
> =CRASH REPORT==== 5-Apr-2020::22:20:32.918000 === crasher:
> initial call: supervisor:sup/1
> pid: <0.280.0>
> registered_name: []
> exception exit: {bad_start_spec,[]}
> in function gen_server:init_it/6 (gen_server.erl, line 358)
> ancestors: [<0.273.0>]
> message_queue_len: 0
> messages: []
> links: [<0.273.0>]
> dictionary: []
> trap_exit: true
> status: running
> heap_size: 376
> stack_size: 27
> reductions: 205 neighbours:
Module
-module(sup).
-behaviour(supervisor).
-compile([export_all]).
start_link() ->
{ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
Pid.
init(_Args) ->
RestartStrategy = {simple_one_for_one, 10, 60},
{ok, {RestartStrategy,[]}}.
add(Sup,Value)-> #adding child
ChildSpec = {
ch1,
{sup, start, [Value]},
permanent,
brutal_kill,
worker,
[ch1]
},
supervisor:start_child(Sup,ChildSpec).
start([Value])-> #child's start_link equivalent (non-genserver)
spawn_link(?MODULE,initworker,[self(),Value]).
initworker(From,Value)-> #child's init
receive
MSG->From ! {got_msg,Value,MSG}
end.
As you using simple_one_for_one, You should define ChildSpec in init and all children use the same ChildSpec .
If you need different, then use 'one_for_one' strategy instead.
For simple_one_for_one:
The second argument of supervisor:start_child/2 must be a list which will be combined with default arguments to child start function arguments defined in ChildSpec.
Here I quickly modified the code to make it work for you.
-module(sup).
-behaviour(supervisor).
-compile([export_all]).
start_link() ->
{ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
Pid.
init(_Args) ->
RestartStrategy = {simple_one_for_one, 10, 60},
ChildSpec = {
ch1,
{sup, start, []},
permanent,
brutal_kill,
worker,
[sup]
},
{ok, {RestartStrategy,[ChildSpec]}}.
add(Sup,Value)->
supervisor:start_child(Sup,[Value]).
start(Value)->
P = spawn(?MODULE, initworker,[]),
P!{self(),Value},
{ok,P}.
initworker()->
receive
{From, MSG} -> io:format(" Value is ~p~n", [MSG])
end.
In the code below, why is the module prime_server not loading?
-module(prime_server).
-behaviour(gen_server).
-export([new_prime/1, start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
new_prime(N) ->
%% 20000 is a timeout (ms)
gen_server:call(?MODULE, {prime, N}, 20000).
init([]) ->
%% Note we must set trap_exit = true if we
%% want terminate/2 to be called when the application
%% is stopped
process_flag(trap_exit, true),
io:format("~p starting~n" ,[?MODULE]),
{ok, 0}.
handle_call({prime, K}, _From, N) ->
{reply, make_new_prime(K), N+1}.
handle_cast(_Msg, N) -> {noreply, N}.
handle_info(_Info, N) -> {noreply, N}.
terminate(_Reason, _N) ->
io:format("~p stopping~n" ,[?MODULE]),
ok.
code_change(_OldVsn, N, _Extra) -> {ok, N}.
make_new_prime(K) ->
if
K > 100 ->
alarm_handler:set_alarm(tooHot),
N = lib_primes:make_prime(K),
alarm_handler:clear_alarm(tooHot),
N;
true ->
lib_primes:make_prime(K)
end.
OUTPUT:
$ erl -boot start_sasl
1>c(prime_server).
{ok,prime_server}
2>c(my_alarm_handler).
my_alarm_handler.erl:2: Warning: undefined callback function code_change/3 (behaviour 'gen_event')
my_alarm_handler.erl:19: Warning: variable 'Reply' is unused
{ok,my_alarm_handler}
3> {ok, Pid} = prime_server:start_link().
prime_server starting
{ok, <0.73.0>}
4> prime_server:new_prime(6).
prime_server stopping
=ERROR REPORT==== 24-Mar-2014::10:59:39 ===
** Generic server prime_server terminating
** Last message in was {prime,6}
** When Server state == 0
** Reason for termination ==
** {'module could not be loaded',
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
=CRASH REPORT==== 24-Mar-2014::10:59:39 ===
crasher:
initial call: prime_server:init/1
pid: <0.73.0>
registered_name: prime_server
exception exit: {undef,
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,
[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]}
in function gen_server:terminate/6 (gen_server.erl, line 744)
ancestors: [<0.71.0>]
messages: []
links: [<0.71.0>]
dictionary: []
trap_exit: true
status: running
heap_size: 610
stack_size: 27
reductions: 181
neighbours:
neighbour: [{pid,<0.71.0>},
{registered_name,[]},
{initial_call,{erlang,apply,2}},
{current_function,{gen,do_call,4}},
{ancestors,[]},
{messages,[]},
{links,[<0.27.0>,<0.73.0>]},
{dictionary,[]},
{trap_exit,false},
{status,waiting},
{heap_size,610},
{stack_size,36},
{reductions,1303}]
** exception exit: undef
in function lib_primes:make_prime/1
called as lib_primes:make_prime(6)
in call from prime_server:handle_call/3 (prime_server.erl, line 21)
in call from gen_server:handle_msg/5 (gen_server.erl, line 585)
in call from proc_lib:init_p_do_apply/3 (proc_lib.erl, line 239)
9> gen_server:call(prime_server, 6).
** exception exit: {noproc,{gen_server,call,[prime_server,6]}}
in function gen_server:call/2 (gen_server.erl, line 180)
10> gen_server:call(prime_server, new_prime, 6).
** exception exit: {noproc,{gen_server,call,[prime_server,new_prime,6]}}
in function gen_server:call/3 (gen_server.erl, line 188)
11> gen_server:call(prime_server, new_prime(5), []).
** exception error: undefined shell command new_prime/1
12> gen_server:call(prime_server, {prime, 4}, []).
** exception exit: {{function_clause,
[{gen,call,
[prime_server,'$gen_call',{prime,4},[]],
[{file,"gen.erl"},{line,147}]},
{gen_server,call,3,
[{file,"gen_server.erl"},{line,184}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,573}]},
{shell,exprs,7,[{file,"shell.erl"},{line,674}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,629}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,614}]}]},
{gen_server,call,[prime_server,{prime,4},[]]}}
{'module could not be loaded',
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
From the log, you use this module lib_primes, do you have this file lib_primes.erl?
I think you should compiule and load this module lib_primes first.
I'm trying to write a ring benchmark where I have N processes and I send a message through them M times. I'd like to store the processes' pid in an ETS table.
-module(ringmets).
-compile(export_all).
start_m(N, M, Msg) ->
ets:new(pid, [ordered_set, named_table]),
List = start_proc(N, M),
%tv:start(),
[ets:insert(pid, {Pid, X}) || {Pid,X} <- lists:zip(lists:seq(1,N), List)],
hd(List) ! {Msg, 1}.
start_proc(0, _M) ->
[];
start_proc(N, M) ->
[spawn(ring_m, loop, [M, N]) | start_proc(N-1, M)].
loop(-1, _N) ->
ok;
loop(M, N) ->
receive
{Msg, CurrPid} ->
case CurrPid == N of
true -> Next = 1;
false -> Next = CurrPid + 1
end,
LU = ets:lookup(pid, Next),
NextPid = element(2, hd(LU)),
NextPid ! {Msg, Next},
loop(M-1, N)
end.
call:
2> ringmets:start_m(5,5,ok).
{ok,1}
3>
=ERROR REPORT==== 22-Nov-2012::19:51:43 ===
Error in process <0.38.0> with exit value: {undef,[{ring_m,loop,[5,5]}]}
=ERROR REPORT==== 22-Nov-2012::19:51:43 ===
Error in process <0.42.0> with exit value: {undef,[{ring_m,loop,[5,1]}]}
=ERROR REPORT==== 22-Nov-2012::19:51:43 ===
Error in process <0.39.0> with exit value: {undef,[{ring_m,loop,[5,4]}]}
=ERROR REPORT==== 22-Nov-2012::19:51:43 ===
Error in process <0.41.0> with exit value: {undef,[{ring_m,loop,[5,2]}]}
=ERROR REPORT==== 22-Nov-2012::19:51:43 ===
Error in process <0.40.0> with exit value: {undef,[{ring_m,loop,[5,3]}]}
loop/2 is exported so I don't understand why I get this error.
your module is ringmets, not ring_m
I'm very new to OTP, I'm trying to create simple example to understand supervisor behaviour:
Here is simple increment server
-module( inc_serv ).
-behaviour( gen_server ).
-export( [ start/0, inc/1, stop/0 ] ).
-export( [ init/1, handle_call/3, terminate/2 ] ).
start() ->
gen_server:start_link( { local, ?MODULE }, ?MODULE, no_args, [] ).
stop() ->
gen_server:call( ?MODULE, stop ).
inc( Num ) ->
gen_server:call( ?MODULE, { num, Num } ).
init( no_args ) ->
io:format( "~p~n", [ "Increment server started :)" ] ),
{ ok, no_state }.
handle_call( { num, Num }, _From, no_state ) ->
{ reply, Num + 1, no_state };
handle_call( stop, _From, no_state ) ->
{ stop, normal, ok, no_state }.
terminate( Reason, no_state ) ->
io:format( "~p~n", [ "Increment server stopped" ] ).
And I'd like to make it supervised by this module:
-module( supervisor_inc ).
-behaviour( supervisor ).
-export( [ start/0 ] ).
-export( [ init/1 ] ).
start() ->
supervisor:start_link( { local, ?MODULE }, ?MODULE, no_args ).
init( no_args ) ->
process_flag( trap_exit, true ),
Supervisor_Spec = { one_for_one, 1, 1 },
IncServ_Spec = {
inc_serv,
{ inc_serv, start, [] },
permanent, 2000, worker, [ inc_serv ] },
{ ok, { Supervisor_Spec, [ IncServ_Spec ] } }.
After that I've performed in erlang shell next steps:
1>
1> c(inc_serv).
{ok,inc_serv}
2>
2> c(supervisor_inc).
{ok,supervisor_inc}
3>
3> supervisor_inc:start().
"Increment server started :)"
{ok,<0.43.0>}
4>
4> inc_serv:inc( 7 ).
8
5> inc_serv:inc( 8 ).
9
After this I've tried next (as I expected I've got error):
6> inc_serv:inc( bad_arg ).
"Increment server stopped"
"Increment server started :)"
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server supervisor_inc terminating
** Last message in was {'EXIT',<0.31.0>,
{{{badarith,
[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,
[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,
[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,
[{file,"shell.erl"},{line,608}]}]}}
** When Server state == {state,
{local,supervisor_inc},
one_for_one,
[{child,<0.48.0>,inc_serv,
{inc_serv,start,[]},
permanent,2000,worker,
[inc_serv]}],
undefined,1,1,
[{1345,739526,107495}],
supervisor_inc,no_args}
** Reason for termination ==
** {{{badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,608}]}]}
** exception exit: {{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
After this I've expected - my supervisor to restart inc_serv. But it didn't:
7> inc_serv:inc( 8 ).
** exception exit: {noproc,{gen_server,call,[inc_serv,{num,8}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
Could you help me to understand what happened? And how should I rewrite my supervisor, to make it able to restart inc_serv
Thanks
This is actually a kind of race condition.
As you might know, the Erlang shell itself is a normal Erlang process. When you start your supervisor from the shell, the supervisor is linked to the shell (because you use supervisor:start_link/3).
When you call your gen_server process, that process crashes (and is correctly restarted by the supervisor, as you can see by the subsequent "Increment server started :)" output).
However, at the same time, your call to gen_server:call/2 will result in the same crash (a gen_server crashing during the call will emit the same crash through the gen_server:call/2 function). This then crashes the shell process, which is linked to your supervisor, which in turn crashes with the same reason (badarith).
Basically, your supervisor is backstabbed by your shell process, after it loyally restarted your gen_server. Like so:
+---------(6)exit----------+ +---------(5)restart---------+
| | | |
| v | v
Shell ---(1)start_link---> supervisor ---(2)start_link---> gen_server
| ^ ^ | ^ | ^
| | | | | | |
| | | +---------(7)exit---------+ | |
| | | | |
| +-------------------------+--------------(4)exit------------+ |
| |
+---------------------------(3)call--------------------------------+
You can avoid this by calling catch inc_serv:inc(bad_arg). in your shell:
90> inc_serv:inc(7).
8
91> catch inc_serv:inc(bad_arg).
"Increment server stopped"
=ERROR REPORT==== 23-Aug-2012::22:10:02 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
"Increment server started :)"
{'EXIT',{{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}}
92> inc_serv:inc(7).
8