gen_server三两话

来源:互联网 发布:windows 2000系统驱动 编辑:程序博客网 时间:2024/05/17 05:14

 from:庆亮的博客 http://www.qingliangcn.com

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

start_link开始:

gen_server.erl

start_link(Name, Mod, Args, Options) ->

gen:start(?MODULE, link, Name, Mod, Args, Options).

 

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

start(GenMod, LinkP, Name, Mod, Args, Options) ->

    case where(Name) of

undefined ->

    do_spawn(GenMod, LinkP, Name, Mod, Args, Options);

Pid ->

    {error, {already_started, Pid}}

end.

再跳:

do_spawn(GenMod, link, Name, Mod, Args, Options) ->

    Time = timeout(Options),

    proc_lib:start_link(?MODULE, init_it,

[GenMod, self(), self(), Name, Mod, Args, Options],

Time,

spawn_opts(Options));

跳到proc_lib.erl文件:

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

    Pid = ?MODULE:spawn_opt(M, F, A, ensure_link(SpawnOpts)),

sync_wait(Pid, Timeout).

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

spawn_opt(M, F, A, Opts) when 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(Parent, Ancestors, Fun) when 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(Class, Reason)

    end.

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

init_p(Parent, Ancestors, M, F, A) when is_atom(M), is_atom(F), is_list(A) ->

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

    put('$initial_call', trans_init(M, F, A)),

    init_p_do_apply(M, F, A).

init_p_do_apply(M, F, A) ->

    try

apply(M, F, A) 

    catch

Class:Reason ->

    exit_p(Class, Reason)

end.

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

gen, init_it,

[gen_server, self(), self(), Name, Mod, Args, Options]

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

调回init

init_it(GenMod, Starter, Parent, Name, Mod, Args, Options) ->

    case name_register(Name) of

true ->

    init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options);

{false, Pid} ->

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

    end.

init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->

    GenMod:init_it(Starter, Parent, Name, Mod, Args, Options).

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

init_it(Starter, self, Name, Mod, Args, Options) ->

    init_it(Starter, self(), Name, Mod, Args, Options);

init_it(Starter, Parent, Name0, Mod, Args, Options) ->

    Name = name(Name0),

    Debug = debug_options(Name, Options),

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

    %% 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(Parent, Name, State, Mod, hibernate, Debug) ->

    proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]);

loop(Parent, Name, State, Mod, Time, Debug) ->

    Msg = receive

      Input ->

    Input

  after Time ->

  timeout

  end,

    decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, false).

decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->

    case Msg of

{system, From, Req} ->

    sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,

  [Name, State, Mod, Time], Hib);

{'EXIT', Parent, Reason} ->

    terminate(Reason, Name, Msg, Mod, State, Debug);

_Msg when Debug =:= [] ->

    handle_msg(Msg, Parent, Name, State, Mod);

_Msg ->

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

      Name, {in, Msg}),

    handle_msg(Msg, Parent, Name, State, Mod, Debug1)

    end.

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

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

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

    Mod:handle_cast(Msg, State);

dispatch(Info, Mod, State) ->

Mod:handle_info(Info, State).

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

handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) ->

case catch Mod:handle_call(Msg, From, State) of

……

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

回头再来看看terminate的处理

terminate(Reason, Name, Msg, Mod, State, Debug) ->

    case catch Mod:terminate(Reason, State) of

{'EXIT', R} ->

    error_info(R, Name, Msg, State, Debug),

    exit(R);

_ ->

    case Reason of

normal ->

    exit(normal);

shutdown ->

    exit(shutdown);

{shutdown,_}=Shutdown ->

    exit(Shutdown);

_ ->

    error_info(Reason, Name, Msg, State, Debug),

    exit(Reason)

    end

end.

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

 

原创粉丝点击