2012-08-23 21 views
9

que soy muy nuevo a OTP, que estoy tratando de crear ejemplo sencillo para entender el comportamiento de supervisor:¿Por qué termina mi supervisor?

Aquí está el servidor incremento simple

-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" ]). 

Y me gustaría que sea supervisado por este módulo:

-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 ] } }. 

Después de que he realizado en el shell de Erlang siguientes pasos:

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 

Después de esto he intentado siguiente (como yo esperaba Tengo 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) 

Después de esto he esperado - mi supervisor para reiniciar inc_serv. Pero no lo hizo:

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) 

¿Podría ayudarme a entender lo que sucedió? Y cómo debería reescribir mi supervisor, para que sea capaz de reiniciar inc_serv

Gracias

Respuesta

22

Esto es en realidad una especie de condición de carrera.

Como ya sabrá, el propio shell de Erlang es un proceso de Erlang normal. Cuando inicia su supervisor desde el shell, el supervisor está vinculado al shell (porque usa supervisor:start_link/3).

Cuando llama a su proceso gen_server, ese proceso se bloquea (y el supervisor lo reinicia correctamente, como puede ver en la siguiente salida "Increment server started :)").

Sin embargo, al mismo tiempo, su llamada a gen_server:call/2 resultará en el mismo accidente (un gen_server estrellarse durante la llamada emitirá el mismo accidente a través de la función de gen_server:call/2). Esto bloquea el proceso de shell, que está vinculado a su supervisor, que a su vez se bloquea por el mismo motivo (badarith).

Básicamente, su supervisor es apuñalado por la espalda por su proceso de shell, después de que lealmente reinició su gen_server. De este modo:

 +---------(6)exit----------+ +---------(5)restart---------+ 
     |       | |       | 
     |       v |       v 
    Shell ---(1)start_link---> supervisor ---(2)start_link---> gen_server 
    |^      ^ |      ^| ^
    | |       | |       | | | 
    | |       | +---------(7)exit---------+ | | 
    | |       |         | | 
    | +-------------------------+--------------(4)exit------------+ | 
    |                 | 
    +---------------------------(3)call--------------------------------+ 

Usted puede evitar esto llamando catch inc_serv:inc(bad_arg). en la 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 
+0

Gracias por una gran respuesta! – stemm

Cuestiones relacionadas