from:庆亮的博客 http://www.qingliangcn.com
gen_server是OTP中的一个重要组成,在开发中出现的频率相当高。弄明白这一块的,对于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_server,LinkP为link
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_server的init_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_cast和handle_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_server的terminate函数后,又exit了一次,目的是为了产生一个'EXIT'消息给supervisor,以便让supervisor做相应的重启策略。