呃,在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进程可以依此消息来做出处理。。。

 

 posted on 2013-05-23 17:15  文武双全大星星  阅读(383)  评论(0编辑  收藏  举报