原创文章,转载请注明: 转载自庆亮的博客-webgame架构

本文链接地址: gen_server三两话

(最近挺忙,都没时间写点啥)

gen_serverOTP中的一个重要组成,在开发中出现的频率相当高。弄明白这一块的,对于gen_server的使用有着相当好的帮忙。下面多数为代码。

start_link开始:

gen_server.erl

start_link(NameModArgsOptions) ->

gen:start(?MODULE, link, NameModArgsOptions).

 

跳到gen.erl,这里的GenMod值为gen_serverLinkPlink

start(GenModLinkPNameModArgsOptions) ->

    case where(Nameof

undefined ->

    do_spawn(GenModLinkPNameModArgsOptions);

Pid ->

    {error, {already_started, Pid}}

end.

再跳:

do_spawn(GenMod, link, NameModArgsOptions) ->

    Time = timeout(Options),

    proc_lib:start_link(?MODULE, init_it,

[GenMod, self(), self(), NameModArgsOptions],

Time,

spawn_opts(Options));

跳到proc_lib.erl文件:

start_link(M,F,A,Timeout,SpawnOptswhen is_atom(M), is_atom(F), is_list(A) ->

    Pid = ?MODULE:spawn_opt(MFA, ensure_link(SpawnOpts)),

sync_wait(PidTimeout).

注意这里有个等待返回,之后需要proc_lib:init_ack来返回消息。

spawn_opt(MFAOptswhen is_atom(M), is_atom(F), is_list(A) ->

    Parent = get_my_name(),

    Ancestors = get_ancestors(),

    check_for_monitor(Opts),

    erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).

这里调用proc_lib模块的init_p方法:

-spec init_p(pid(), [pid()], function()) -> term().

init_p(ParentAncestorsFunwhen is_function(Fun) ->

    put('$ancestors', [Parent|Ancestors]),

    {module,Mod} = erlang:fun_info(Fun, module),

    {name,Name} = erlang:fun_info(Fun, name),

    {arity,Arity} = erlang:fun_info(Fun, arity),

    put('$initial_call', {Mod,Name,Arity}),

    try

Fun()

    catch

Class:Reason ->

    exit_p(ClassReason)

    end.

-spec init_p(pid(), [pid()], atom(), atom(), [term()]) -> term().

init_p(ParentAncestorsMFAwhen is_atom(M), is_atom(F), is_list(A) ->

    put('$ancestors', [Parent|Ancestors]),

    put('$initial_call', trans_init(MFA)),

    init_p_do_apply(MFA).

init_p_do_apply(MFA) ->

    try

apply(MFA

    catch

Class:Reason ->

    exit_p(ClassReason)

end.

有点晕头了吧,MFA是什么呢?如下:

gen, init_it,

[gen_server, self(), self(), NameModArgsOptions]

这里的NameModArgs是我们自己的注册名,模块名、参数。

调回init

init_it(GenModStarterParentNameModArgsOptions) ->

    case name_register(Nameof

true ->

    init_it2(GenModStarterParentNameModArgsOptions);

{false, Pid} ->

    proc_lib:init_ack(Starter, {error, {already_started, Pid}})

    end.

init_it2(GenModStarterParentNameModArgsOptions) ->

    GenMod:init_it(StarterParentNameModArgsOptions).

ok,又回到gen_server了。跳转到gen_serverinit_it

init_it(Starter, self, NameModArgsOptions) ->

    init_it(Starter, self(), NameModArgsOptions);

init_it(StarterParentName0ModArgsOptions) ->

    Name = name(Name0),

    Debug = debug_options(NameOptions),

    case catch Mod:init(Argsof

{ok, State} ->

    proc_lib:init_ack(Starter, {ok, self()}),      

    loop(ParentNameStateMod, infinity, Debug);

{ok, StateTimeout} ->

    proc_lib:init_ack(Starter, {ok, self()}),      

    loop(ParentNameStateModTimeoutDebug);

{stop, Reason} ->

    %% For consistency, we must make sure that the

    %% registered name (if any) is unregistered before

    %% the parent process is notified about the failure.

    %% (Otherwise, the parent process could get

    %% an 'already_started' error if it immediately

    %% tried starting the process again.)

    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.

呵呵,苦尽甘来,终于来到loop主循环了。 gen_server的消息怎么来的?就是这个loop不断地取出消息、decode_msg、再loop来不断从消息队列中取出消息的。这个主循环就是我们的gen_server进程了。

loop(ParentNameStateMod, hibernate, Debug) ->

    proc_lib:hibernate(?MODULE,wake_hib,[ParentNameStateModDebug]);

loop(ParentNameStateModTimeDebug) ->

    Msg = receive

      Input ->

    Input

  after Time ->

  timeout

  end,

    decode_msg(MsgParentNameStateModTimeDebug, false).

decode_msg(MsgParentNameStateModTimeDebugHib) ->

    case Msg of

{system, FromReq} ->

    sys:handle_system_msg(ReqFromParent?MODULEDebug,

  [NameStateModTime], Hib);

{'EXIT', ParentReason} ->

    terminate(ReasonNameMsgModStateDebug);

_Msg when Debug =:= [] ->

    handle_msg(MsgParentNameStateMod);

_Msg ->

    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, 

      Name, {in, Msg}),

    handle_msg(MsgParentNameStateModDebug1)

    end.

这里我们就看到了terminate的自动回调(如果设置了trap_exit)。

handle_msg中处理了handle_call类型的调用, 而handle_casthandle_info则由dispatch来处理:

dispatch({'$gen_cast', Msg}, ModState) ->

    Mod:handle_cast(MsgState);

dispatch(InfoModState) ->

Mod:handle_info(InfoState).

handle_msg处理handle_call也很简单,回调我们的handle_call方法:

handle_msg({'$gen_call', FromMsg}, ParentNameStateModDebug) ->

case catch Mod:handle_call(MsgFromStateof

……

至此,整个gen_server的运行流程就清楚了(晕的话,多看几遍)。

回头再来看看terminate的处理

terminate(ReasonNameMsgModStateDebug) ->

    case catch Mod:terminate(ReasonStateof

{'EXIT', R} ->

    error_info(RNameMsgStateDebug),

    exit(R);

_ ->

    case Reason of

normal ->

    exit(normal);

shutdown ->

    exit(shutdown);

{shutdown,_}=Shutdown ->

    exit(Shutdown);

_ ->

    error_info(ReasonNameMsgStateDebug),

    exit(Reason)

    end

end.

这里terminate在回调了gen_serverterminate函数后,又exit了一次,目的是为了产生一个'EXIT'消息给supervisor,以便让supervisor做相应的重启策略。