[erlang-questions] clarify: gen_server:start_link/3 return values?

Bengt Kleberg bengt.kleberg@REDACTED
Thu Apr 25 14:45:11 CEST 2013


Would it not be consistent with the documentation if the return values
{stop, Reason} or ignore, from init/1, would not cause a call to
erlang:exit/1 ?


bengt

On Thu, 2013-04-25 at 12:04 +0300, Aggelos Giantsios wrote:
> 
> On Thu, Apr 25, 2013 at 11:19 AM, Bengt Kleberg
> <bengt.kleberg@REDACTED> wrote:
>         
>         Under what circumstances will gen_server:start_link/3 return
>         {error,
>         Reason}?
> 
> You are right, there is an inconsistency in the documentation.
> So let's have a look at the code of gen_server:start_link/3.
> 
> In gen_server.erl
> ------------------------
> start_link(Mod, Args, Options) ->
>     gen:start(?MODULE, link, Mod, Args, Options).
> 
> In gen.erl
> --------------
> start(GenMod, LinkP, Mod, Args, Options) ->
>     do_spawn(GenMod, LinkP, Mod, Args, Options).
> 
> %% We only need this clause as LinkP =:= link in our case
> do_spawn(GenMod, link, Mod, Args, Options) ->
>     Time = timeout(Options),
>     proc_lib:start_link(?MODULE, init_it,
>                               [GenMod, self(), self(), Mod, Args,
> Options],
>                               Time,
>                               spawn_opts(Options));
> 
> init_it(GenMod, Starter, Parent, Mod, Args, Options) ->
>     init_it2(GenMod, Starter, Parent, self(), Mod, Args, Options).
> 
> init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
>     %% So in our case will call gen_server:init_it/6 where Parent will
> be the a PID
>     GenMod:init_it(Starter, Parent, Name, Mod, Args, Options).
> 
> In gen_server.erl
> ------------------------
> init_it(Starter, Parent, Name0, Mod, Args, Options) ->
>     Name = name(Name0),
>     Debug = debug_options(Name, Options),
>     %% The actual call of our module's init function
>     case catch Mod:init(Args) of
>     {ok, State} ->
>         proc_lib:init_ack(Starter, {ok, self()}),
>         loop(Parent, Name, State, Mod, infinity, Debug);
>     {ok, State, Timeout} ->
>         proc_lib:init_ack(Starter, {ok, self()}),
>         loop(Parent, Name, State, Mod, Timeout, Debug);
>     {stop, Reason} ->
>         unregister_name(Name0),
>         proc_lib:init_ack(Starter, {error, Reason}),
>         exit(Reason);
>     ignore ->
>         unregister_name(Name0),
>         proc_lib:init_ack(Starter, ignore),
>         exit(normal);
>     {'EXIT', Reason} ->
>         unregister_name(Name0),
>         proc_lib:init_ack(Starter, {error, Reason}),
>         exit(Reason);
>     Else ->
>         Error = {bad_return_value, Else},
>         proc_lib:init_ack(Starter, {error, Error}),
>         exit(Error)
>     end.
> 
> So we see that if our module's init function returns {stop, Reason} or
> exits with Reason then proc_lib:init_ack(Starter, {error, Reason})
> will make proc_lib:start_link return {error, Reason}, but then
> exit(Reason) will actually cause the spawned server process to crash
> and subsequently the process that called gen_server:start_link.
> The behaviour you notice is thus completely justified and the only way
> that the behaviour the documentation describes is accurate is when
> your process traps exits and not allows exit(Reason) to crash it.
> 
> Sorry for the long post! I hope this cleared things up!
> 
> Aggelos
> 




More information about the erlang-questions mailing list