呃,在gen_event中有两个添加handler的方法
gen_event:add_handler/3 gen_event:add_sup_handler/3
一开始总是有些迷惑两者的区别,今天查看了gen_event源码,总算弄清两者的区别。
add_handler添加的只是把gen_event作为容器,仅仅在适当的时间调用模块的回调函数。(后面我描述为normal handler)
而add_sup_hander除了在适当的时间调用模块的回调函数以外,同时link到调用该函数的进程。(后面我描述为supervised handler)
首先看看在gen_event中描述handler的record
-record(handler, {module :: atom(), id = false, state, supervised = false :: 'false' | pid()}).
supervised字段用来区分是那种类型的handler,
首先我们查看一下,添加两种类型的handler实际做了什么事。
%% server_add_handler(Handler, Args, MSL) -> {Ret, MSL'}. %% where MSL = [#handler{}] %% Ret goes to the top level MSL' is the new internal state of the %% event handler server_add_handler({Mod,Id}, Args, MSL) -> Handler = #handler{module = Mod, id = Id}, server_add_handler(Mod, Handler, Args, MSL); server_add_handler(Mod, Args, MSL) -> Handler = #handler{module = Mod}, server_add_handler(Mod, Handler, Args, MSL). server_add_handler(Mod, Handler, Args, MSL) -> case catch Mod:init(Args) of {ok, State} -> {false, ok, [Handler#handler{state = State}|MSL]}; {ok, State, hibernate} -> {true, ok, [Handler#handler{state = State}|MSL]}; Other -> {false, Other, MSL} end. %% Set up a link to the supervising process. %% (Ought to be unidirected links here, Erl5.0 !!) %% NOTE: This link will not be removed then the %% handler is removed in case another handler has %% own link to this process. server_add_sup_handler({Mod,Id}, Args, MSL, Parent) -> link(Parent), Handler = #handler{module = Mod, id = Id, supervised = Parent}, server_add_handler(Mod, Handler, Args, MSL); server_add_sup_handler(Mod, Args, MSL, Parent) -> link(Parent), Handler = #handler{module = Mod, supervised = Parent}, server_add_handler(Mod, Handler, Args, MSL).
从上面的代码可以看到,如果添加的是normal handler,supervised保持为默认的'false',
如果为supervised handler,则存放调用改函数时候的进程id。
最后,都调用了Mod:init/1函数,将生成handler添加进handlers 列表中。
接下来,我们再看看当gen_event接受到'EXIT'消息时候,做的处理。
%% First terminate the supervised (if exists) handlers and %% then inform other handlers. %% We do not know if any handler really is interested but it %% may be so ! handle_exit(From, Reason, MSL, SName) -> MSL1 = terminate_supervised(From, Reason, MSL, SName), {_,MSL2}=server_notify({'EXIT', From, Reason}, handle_info, MSL1, SName), MSL2. terminate_supervised(Pid, Reason, MSL, SName) -> F = fun(Ha) when Ha#handler.supervised =:= Pid -> do_terminate(Ha#handler.module, Ha, {stop,Reason}, Ha#handler.state, {parent_terminated, {Pid,Reason}}, SName, shutdown), false; (_) -> true end, lists:filter(F, MSL). do_terminate(Mod, Handler, Args, State, LastIn, SName, Reason) -> Res = (catch Mod:terminate(Args, State)), report_terminate(Handler, Reason, Args, State, LastIn, SName, Res), Res.
因为在添加supervised handler时候,我们link到了调用者进程。
所以当收到'EXIT'通知消息的时候,我们必须判断是否为supervised 进程的终止消息,如果是的话,需要删除handler列表中的该supervised handler。
同时调用Mod:terminate/2函数,并通知其他的handlers。
当停止gen_event时候:
terminate_server(Reason, Parent, MSL, ServerName) -> stop_handlers(MSL, ServerName), do_unlink(Parent, MSL), exit(Reason). %% unlink the supervisor process of all supervised handlers. %% We do not want a handler supervisor to EXIT due to the %% termination of the event manager (server). %% Do not unlink Parent ! do_unlink(Parent, MSL) -> lists:foreach(fun(Handler) when Handler#handler.supervised =:= Parent -> true; (Handler) when is_pid(Handler#handler.supervised) -> unlink(Handler#handler.supervised), true; (_) -> true end, MSL).
我们也需要判断supervised handler,对进程进行unlink,当gen_event进程结束时候,并不会强制supervisored handler进程结束。
而在下面两个函数调用时候:
gen_event:delete_handler/3 gen_event:swap_sup_handler/3
除了正常的逻辑,同时向supervisored handler的宿主进程发送{gen_event_EXIT,Handler,Reason},supervised handler进程可以依此消息来做出处理。。。