aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/src/gen_event.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/stdlib/src/gen_event.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/stdlib/src/gen_event.erl')
-rw-r--r--lib/stdlib/src/gen_event.erl721
1 files changed, 721 insertions, 0 deletions
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
new file mode 100644
index 0000000000..1b30aaf5eb
--- /dev/null
+++ b/lib/stdlib/src/gen_event.erl
@@ -0,0 +1,721 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(gen_event).
+
+%%%
+%%% A general event handler.
+%%% Several handlers (functions) can be added.
+%%% Each handler holds a state and will be called
+%%% for every event received of the handler.
+%%%
+
+%%% Modified by Magnus.
+%%% Take care of fault situations and made notify asynchronous.
+%%% Re-written by Joe with new functional interface !
+%%% Modified by Martin - uses proc_lib, sys and gen!
+
+
+-export([start/0, start/1, start_link/0, start_link/1, stop/1, notify/2,
+ sync_notify/2,
+ add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3,
+ swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/4]).
+
+-export([behaviour_info/1]).
+
+-export([init_it/6,
+ system_continue/3,
+ system_terminate/4,
+ system_code_change/4,
+ print_event/3,
+ format_status/2]).
+
+-import(error_logger, [error_msg/2]).
+
+-define(reply(X), From ! {element(2,Tag), X}).
+
+-record(handler, {module :: atom(),
+ id = false,
+ state,
+ supervised = false :: 'false' | pid()}).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+
+-spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}].
+
+behaviour_info(callbacks) ->
+ [{init,1},{handle_event,2},{handle_call,2},{handle_info,2},
+ {terminate,2},{code_change,3}];
+behaviour_info(_Other) ->
+ undefined.
+
+%% gen_event:start(Handler) -> {ok, Pid} | {error, What}
+%% gen_event:add_handler(Handler, Mod, Args) -> ok | Other
+%% gen_event:notify(Handler, Event) -> ok
+%% gen_event:call(Handler, Mod, Query) -> {ok, Val} | {error, Why}
+%% gen_event:call(Handler, Mod, Query, Timeout) -> {ok, Val} | {error, Why}
+%% gen_event:delete_handler(Handler, Mod, Args) -> Val
+%% gen_event:swap_handler(Handler, {OldMod, Args1}, {NewMod, Args2}) -> ok
+%% gen_event:which_handler(Handler) -> [Mod]
+%% gen_event:stop(Handler) -> ok
+
+
+%% handlers must export
+%% Mod:init(Args) -> {ok, State} | Other
+%% Mod:handle_event(Event, State) ->
+%% {ok, State'} | remove_handler | {swap_handler,Args1,State1,Mod2,Args2}
+%% Mod:handle_info(Info, State) ->
+%% {ok, State'} | remove_handler | {swap_handler,Args1,State1,Mod2,Args2}
+%% Mod:handle_call(Query, State) ->
+%% {ok, Reply, State'} | {remove_handler, Reply} |
+%% {swap_handler, Reply, Args1,State1,Mod2,Args2}
+%% Mod:terminate(Args, State) -> Val
+
+
+%% add_handler(H, Mod, Args) -> ok | Other
+%% Mod:init(Args) -> {ok, State} | Other
+
+%% delete_handler(H, Mod, Args) -> Val
+%% Mod:terminate(Args, State) -> Val
+
+%% notify(H, Event)
+%% Mod:handle_event(Event, State) ->
+%% {ok, State1}
+%% remove_handler
+%% Mod:terminate(remove_handler, State) is called
+%% the return value is ignored
+%% {swap_handler, Args1, State1, Mod2, Args2}
+%% State2 = Mod:terminate(Args1, State1) is called
+%% the return value is chained into the new module and
+%% Mod2:init({Args2, State2}) is called
+%% Other
+%% Mod:terminate({error, Other}, State) is called
+%% The return value is ignored
+%% call(H, Mod, Query) -> Val
+%% call(H, Mod, Query, Timeout) -> Val
+%% Mod:handle_call(Query, State) -> as above
+
+%%---------------------------------------------------------------------------
+
+-type handler() :: atom() | {atom(), term()}.
+-type emgr_name() :: {'local', atom()} | {'global', atom()}.
+-type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid().
+-type start_ret() :: {'ok', pid()} | {'error', term()}.
+
+%%---------------------------------------------------------------------------
+
+-define(NO_CALLBACK, 'no callback module').
+
+-spec start() -> start_ret().
+start() ->
+ gen:start(?MODULE, nolink, ?NO_CALLBACK, [], []).
+
+-spec start(emgr_name()) -> start_ret().
+start(Name) ->
+ gen:start(?MODULE, nolink, Name, ?NO_CALLBACK, [], []).
+
+-spec start_link() -> start_ret().
+start_link() ->
+ gen:start(?MODULE, link, ?NO_CALLBACK, [], []).
+
+-spec start_link(emgr_name()) -> start_ret().
+start_link(Name) ->
+ gen:start(?MODULE, link, Name, ?NO_CALLBACK, [], []).
+
+%% -spec init_it(pid(), 'self' | pid(), emgr_name(), module(), [term()], [_]) ->
+init_it(Starter, self, Name, Mod, Args, Options) ->
+ init_it(Starter, self(), Name, Mod, Args, Options);
+init_it(Starter, Parent, Name0, _, _, Options) ->
+ process_flag(trap_exit, true),
+ Debug = gen:debug_options(Options),
+ proc_lib:init_ack(Starter, {ok, self()}),
+ Name = name(Name0),
+ loop(Parent, Name, [], Debug, false).
+
+name({local,Name}) -> Name;
+name({global,Name}) -> Name;
+name(Pid) when is_pid(Pid) -> Pid.
+
+-spec add_handler(emgr_ref(), handler(), term()) -> term().
+add_handler(M, Handler, Args) -> rpc(M, {add_handler, Handler, Args}).
+
+-spec add_sup_handler(emgr_ref(), handler(), term()) -> term().
+add_sup_handler(M, Handler, Args) ->
+ rpc(M, {add_sup_handler, Handler, Args, self()}).
+
+-spec notify(emgr_ref(), term()) -> 'ok'.
+notify(M, Event) -> send(M, {notify, Event}).
+
+-spec sync_notify(emgr_ref(), term()) -> 'ok'.
+sync_notify(M, Event) -> rpc(M, {sync_notify, Event}).
+
+-spec call(emgr_ref(), handler(), term()) -> term().
+call(M, Handler, Query) -> call1(M, Handler, Query).
+
+-spec call(emgr_ref(), handler(), term(), timeout()) -> term().
+call(M, Handler, Query, Timeout) -> call1(M, Handler, Query, Timeout).
+
+-spec delete_handler(emgr_ref(), handler(), term()) -> term().
+delete_handler(M, Handler, Args) -> rpc(M, {delete_handler, Handler, Args}).
+
+-spec swap_handler(emgr_ref(), {handler(), term()}, {handler(), term()}) ->
+ 'ok' | {'error', term()}.
+swap_handler(M, {H1, A1}, {H2, A2}) -> rpc(M, {swap_handler, H1, A1, H2, A2}).
+
+-spec swap_sup_handler(emgr_ref(), {handler(), term()}, {handler(), term()}) ->
+ 'ok' | {'error', term()}.
+swap_sup_handler(M, {H1, A1}, {H2, A2}) ->
+ rpc(M, {swap_sup_handler, H1, A1, H2, A2, self()}).
+
+-spec which_handlers(emgr_ref()) -> [handler()].
+which_handlers(M) -> rpc(M, which_handlers).
+
+-spec stop(emgr_ref()) -> 'ok'.
+stop(M) -> rpc(M, stop).
+
+rpc(M, Cmd) ->
+ {ok, Reply} = gen:call(M, self(), Cmd, infinity),
+ Reply.
+
+call1(M, Handler, Query) ->
+ Cmd = {call, Handler, Query},
+ try gen:call(M, self(), Cmd) of
+ {ok, Res} ->
+ Res
+ catch
+ exit:Reason ->
+ exit({Reason, {?MODULE, call, [M, Handler, Query]}})
+ end.
+
+call1(M, Handler, Query, Timeout) ->
+ Cmd = {call, Handler, Query},
+ try gen:call(M, self(), Cmd, Timeout) of
+ {ok, Res} ->
+ Res
+ catch
+ exit:Reason ->
+ exit({Reason, {?MODULE, call, [M, Handler, Query, Timeout]}})
+ end.
+
+send({global, Name}, Cmd) ->
+ catch global:send(Name, Cmd),
+ ok;
+send(M, Cmd) ->
+ M ! Cmd,
+ ok.
+
+loop(Parent, ServerName, MSL, Debug, true) ->
+ proc_lib:hibernate(?MODULE, wake_hib, [Parent, ServerName, MSL, Debug]);
+loop(Parent, ServerName, MSL, Debug, _) ->
+ fetch_msg(Parent, ServerName, MSL, Debug, false).
+
+wake_hib(Parent, ServerName, MSL, Debug) ->
+ fetch_msg(Parent, ServerName, MSL, Debug, true).
+
+fetch_msg(Parent, ServerName, MSL, Debug, Hib) ->
+ receive
+ {system, From, Req} ->
+ sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
+ [ServerName, MSL, Hib],Hib);
+ {'EXIT', Parent, Reason} ->
+ terminate_server(Reason, Parent, MSL, ServerName);
+ Msg when Debug =:= [] ->
+ handle_msg(Msg, Parent, ServerName, MSL, []);
+ Msg ->
+ Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
+ ServerName, {in, Msg}),
+ handle_msg(Msg, Parent, ServerName, MSL, Debug1)
+ end.
+
+handle_msg(Msg, Parent, ServerName, MSL, Debug) ->
+ case Msg of
+ {notify, Event} ->
+ {Hib,MSL1} = server_notify(Event, handle_event, MSL, ServerName),
+ loop(Parent, ServerName, MSL1, Debug, Hib);
+ {From, Tag, {sync_notify, Event}} ->
+ {Hib, MSL1} = server_notify(Event, handle_event, MSL, ServerName),
+ ?reply(ok),
+ loop(Parent, ServerName, MSL1, Debug, Hib);
+ {'EXIT', From, Reason} ->
+ MSL1 = handle_exit(From, Reason, MSL, ServerName),
+ loop(Parent, ServerName, MSL1, Debug, false);
+ {From, Tag, {call, Handler, Query}} ->
+ {Hib, Reply, MSL1} = server_call(Handler, Query, MSL, ServerName),
+ ?reply(Reply),
+ loop(Parent, ServerName, MSL1, Debug, Hib);
+ {From, Tag, {add_handler, Handler, Args}} ->
+ {Hib, Reply, MSL1} = server_add_handler(Handler, Args, MSL),
+ ?reply(Reply),
+ loop(Parent, ServerName, MSL1, Debug, Hib);
+ {From, Tag, {add_sup_handler, Handler, Args, SupP}} ->
+ {Hib, Reply, MSL1} = server_add_sup_handler(Handler, Args, MSL, SupP),
+ ?reply(Reply),
+ loop(Parent, ServerName, MSL1, Debug, Hib);
+ {From, Tag, {delete_handler, Handler, Args}} ->
+ {Reply, MSL1} = server_delete_handler(Handler, Args, MSL,
+ ServerName),
+ ?reply(Reply),
+ loop(Parent, ServerName, MSL1, Debug, false);
+ {From, Tag, {swap_handler, Handler1, Args1, Handler2, Args2}} ->
+ {Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2,
+ Args2, MSL, ServerName),
+ ?reply(Reply),
+ loop(Parent, ServerName, MSL1, Debug, Hib);
+ {From, Tag, {swap_sup_handler, Handler1, Args1, Handler2, Args2,
+ Sup}} ->
+ {Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2,
+ Args2, MSL, Sup, ServerName),
+ ?reply(Reply),
+ loop(Parent, ServerName, MSL1, Debug, Hib);
+ {From, Tag, stop} ->
+ catch terminate_server(normal, Parent, MSL, ServerName),
+ ?reply(ok);
+ {From, Tag, which_handlers} ->
+ ?reply(the_handlers(MSL)),
+ loop(Parent, ServerName, MSL, Debug, false);
+ {From, Tag, get_modules} ->
+ ?reply(get_modules(MSL)),
+ loop(Parent, ServerName, MSL, Debug, false);
+ Other ->
+ {Hib, MSL1} = server_notify(Other, handle_info, MSL, ServerName),
+ loop(Parent, ServerName, MSL1, Debug, Hib)
+ end.
+
+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).
+
+%% 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).
+
+%%-----------------------------------------------------------------
+%% Callback functions for system messages handling.
+%%-----------------------------------------------------------------
+system_continue(Parent, Debug, [ServerName, MSL, Hib]) ->
+ loop(Parent, ServerName, MSL, Debug, Hib).
+
+-spec system_terminate(_, _, _, [_]) -> no_return().
+system_terminate(Reason, Parent, _Debug, [ServerName, MSL, _Hib]) ->
+ terminate_server(Reason, Parent, MSL, ServerName).
+
+%%-----------------------------------------------------------------
+%% Module here is sent in the system msg change_code. It specifies
+%% which module should be changed.
+%%-----------------------------------------------------------------
+system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) ->
+ MSL1 = lists:zf(fun(H) when H#handler.module =:= Module ->
+ {ok, NewState} =
+ Module:code_change(OldVsn,
+ H#handler.state, Extra),
+ {true, H#handler{state = NewState}};
+ (_) -> true
+ end,
+ MSL),
+ {ok, [ServerName, MSL1, Hib]}.
+
+%%-----------------------------------------------------------------
+%% Format debug messages. Print them as the call-back module sees
+%% them, not as the real erlang messages. Use trace for that.
+%%-----------------------------------------------------------------
+print_event(Dev, {in, Msg}, Name) ->
+ case Msg of
+ {notify, Event} ->
+ io:format(Dev, "*DBG* ~p got event ~p~n", [Name, Event]);
+ {_,_,{call, Handler, Query}} ->
+ io:format(Dev, "*DBG* ~p(~p) got call ~p~n",
+ [Name, Handler, Query]);
+ _ ->
+ io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
+ end;
+print_event(Dev, Dbg, Name) ->
+ io:format(Dev, "*DBG* ~p : ~p~n", [Name, Dbg]).
+
+
+%% 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).
+
+%% server_delete_handler(HandlerId, Args, MSL) -> {Ret, MSL'}
+
+server_delete_handler(HandlerId, Args, MSL, SName) ->
+ case split(HandlerId, MSL) of
+ {Mod, Handler, MSL1} ->
+ {do_terminate(Mod, Handler, Args,
+ Handler#handler.state, delete, SName, normal),
+ MSL1};
+ error ->
+ {{error, module_not_found}, MSL}
+ end.
+
+%% server_swap_handler(Handler1, Args1, Handler2, Args2, MSL, SN) -> MSL'
+%% server_swap_handler(Handler1, Args1, Handler2, Args2, MSL, Sup, SN) -> MSL'
+
+server_swap_handler(Handler1, Args1, Handler2, Args2, MSL, SName) ->
+ {State2, Sup, MSL1} = split_and_terminate(Handler1, Args1, MSL,
+ SName, Handler2, false),
+ case s_s_h(Sup, Handler2, {Args2, State2}, MSL1) of
+ {Hib, ok, MSL2} ->
+ {Hib, ok, MSL2};
+ {Hib, What, MSL2} ->
+ {Hib, {error, What}, MSL2}
+ end.
+
+server_swap_handler(Handler1, Args1, Handler2, Args2, MSL, Sup, SName) ->
+ {State2, _, MSL1} = split_and_terminate(Handler1, Args1, MSL,
+ SName, Handler2, Sup),
+ case s_s_h(Sup, Handler2, {Args2, State2}, MSL1) of
+ {Hib, ok, MSL2} ->
+ {Hib, ok, MSL2};
+ {Hib, What, MSL2} ->
+ {Hib, {error, What}, MSL2}
+ end.
+
+s_s_h(false, Handler, Args, MSL) ->
+ server_add_handler(Handler, Args, MSL);
+s_s_h(Pid, Handler, Args, MSL) ->
+ server_add_sup_handler(Handler, Args, MSL, Pid).
+
+split_and_terminate(HandlerId, Args, MSL, SName, Handler2, Sup) ->
+ case split(HandlerId, MSL) of
+ {Mod, Handler, MSL1} ->
+ OldSup = Handler#handler.supervised,
+ NewSup = if
+ not Sup -> OldSup;
+ true -> Sup
+ end,
+ {do_terminate(Mod, Handler, Args,
+ Handler#handler.state, swapped, SName,
+ {swapped, Handler2, NewSup}),
+ OldSup,
+ MSL1};
+ error ->
+ {error, false, MSL}
+ end.
+
+%% server_notify(Event, Func, MSL, SName) -> MSL'
+
+server_notify(Event, Func, [Handler|T], SName) ->
+ case server_update(Handler, Func, Event, SName) of
+ {ok, Handler1} ->
+ {Hib, NewHandlers} = server_notify(Event, Func, T, SName),
+ {Hib, [Handler1|NewHandlers]};
+ {hibernate, Handler1} ->
+ {_Hib, NewHandlers} = server_notify(Event, Func, T, SName),
+ {true, [Handler1|NewHandlers]};
+ no ->
+ server_notify(Event, Func, T, SName)
+ end;
+server_notify(_, _, [], _) ->
+ {false, []}.
+
+%% server_update(Handler, Func, Event, ServerName) -> Handler1 | no
+
+server_update(Handler1, Func, Event, SName) ->
+ Mod1 = Handler1#handler.module,
+ State = Handler1#handler.state,
+ case catch Mod1:Func(Event, State) of
+ {ok, State1} ->
+ {ok, Handler1#handler{state = State1}};
+ {ok, State1, hibernate} ->
+ {hibernate, Handler1#handler{state = State1}};
+ {swap_handler, Args1, State1, Handler2, Args2} ->
+ do_swap(Mod1, Handler1, Args1, State1, Handler2, Args2, SName);
+ remove_handler ->
+ do_terminate(Mod1, Handler1, remove_handler, State,
+ remove, SName, normal),
+ no;
+ Other ->
+ do_terminate(Mod1, Handler1, {error, Other}, State,
+ Event, SName, crash),
+ no
+ end.
+
+do_swap(Mod1, Handler1, Args1, State1, Handler2, Args2, SName) ->
+ %% finalise the existing handler
+ State2 = do_terminate(Mod1, Handler1, Args1, State1,
+ swapped, SName,
+ {swapped, Handler2, Handler1#handler.supervised}),
+ {Mod2, Handler} = new_handler(Handler2, Handler1),
+ case catch Mod2:init({Args2, State2}) of
+ {ok, State2a} ->
+ {ok, Handler#handler{state = State2a}};
+ Other ->
+ report_terminate(Handler, crash, {error, Other}, SName, false),
+ no
+ end.
+
+new_handler({Mod,Id}, Handler1) ->
+ {Mod, #handler{module = Mod,
+ id = Id,
+ supervised = Handler1#handler.supervised}};
+new_handler(Mod, Handler1) ->
+ {Mod, #handler{module = Mod,
+ supervised = Handler1#handler.supervised}}.
+
+
+-spec split(handler(), [#handler{}]) ->
+ {atom(), #handler{}, [#handler{}]} | 'error'.
+
+split(Ha, MSL) -> split(Ha, MSL, []).
+
+split({Mod,Id}, [Ha|T], L) when Ha#handler.module =:= Mod,
+ Ha#handler.id =:= Id ->
+ {Mod, Ha, lists:reverse(L, T)};
+split(Mod, [Ha|T], L) when Ha#handler.module =:= Mod,
+ not Ha#handler.id ->
+ {Mod, Ha, lists:reverse(L, T)};
+split(Ha, [H|T], L) ->
+ split(Ha, T, [H|L]);
+split(_, [], _) ->
+ error.
+
+%% server_call(Handler, Query, MSL, ServerName) ->
+%% {Reply, MSL1}
+
+server_call(Handler, Query, MSL, SName) ->
+ case search(Handler, MSL) of
+ {ok, Ha} ->
+ case server_call_update(Ha, Query, SName) of
+ {no, Reply} ->
+ {false, Reply, delete(Handler, MSL)};
+ {{ok, Ha1}, Reply} ->
+ {false, Reply, replace(Handler, MSL, Ha1)};
+ {{hibernate, Ha1}, Reply} ->
+ {true, Reply, replace(Handler, MSL, Ha1)}
+ end;
+ false ->
+ {false, {error, bad_module}, MSL}
+ end.
+
+search({Mod, Id}, [Ha|_MSL]) when Ha#handler.module =:= Mod,
+ Ha#handler.id =:= Id ->
+ {ok, Ha};
+search(Mod, [Ha|_MSL]) when Ha#handler.module =:= Mod,
+ not Ha#handler.id ->
+ {ok, Ha};
+search(Handler, [_|MSL]) ->
+ search(Handler, MSL);
+search(_, []) ->
+ false.
+
+delete({Mod, Id}, [Ha|MSL]) when Ha#handler.module =:= Mod,
+ Ha#handler.id =:= Id ->
+ MSL;
+delete(Mod, [Ha|MSL]) when Ha#handler.module =:= Mod,
+ not Ha#handler.id ->
+ MSL;
+delete(Handler, [Ha|MSL]) ->
+ [Ha|delete(Handler, MSL)];
+delete(_, []) ->
+ [].
+
+replace({Mod, Id}, [Ha|MSL], NewHa) when Ha#handler.module =:= Mod,
+ Ha#handler.id =:= Id ->
+ [NewHa|MSL];
+replace(Mod, [Ha|MSL], NewHa) when Ha#handler.module =:= Mod,
+ not Ha#handler.id ->
+ [NewHa|MSL];
+replace(Handler, [Ha|MSL], NewHa) ->
+ [Ha|replace(Handler, MSL, NewHa)];
+replace(_, [], NewHa) ->
+ [NewHa].
+
+%% server_call_update(Handler, Query, ServerName) ->
+%% {{Handler1, State1} | 'no', Reply}
+
+server_call_update(Handler1, Query, SName) ->
+ Mod1 = Handler1#handler.module,
+ State = Handler1#handler.state,
+ case catch Mod1:handle_call(Query, State) of
+ {ok, Reply, State1} ->
+ {{ok, Handler1#handler{state = State1}}, Reply};
+ {ok, Reply, State1, hibernate} ->
+ {{hibernate, Handler1#handler{state = State1}},
+ Reply};
+ {swap_handler, Reply, Args1, State1, Handler2, Args2} ->
+ {do_swap(Mod1,Handler1,Args1,State1,Handler2,Args2,SName), Reply};
+ {remove_handler, Reply} ->
+ do_terminate(Mod1, Handler1, remove_handler, State,
+ remove, SName, normal),
+ {no, Reply};
+ Other ->
+ do_terminate(Mod1, Handler1, {error, Other}, State,
+ Query, SName, crash),
+ {no, {error, Other}}
+ end.
+
+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.
+
+report_terminate(Handler, crash, {error, Why}, State, LastIn, SName, _) ->
+ report_terminate(Handler, Why, State, LastIn, SName);
+report_terminate(Handler, How, _, State, LastIn, SName, _) ->
+ %% How == normal | shutdown | {swapped, NewHandler, NewSupervisor}
+ report_terminate(Handler, How, State, LastIn, SName).
+
+report_terminate(Handler, Reason, State, LastIn, SName) ->
+ report_error(Handler, Reason, State, LastIn, SName),
+ case Handler#handler.supervised of
+ false ->
+ ok;
+ Pid ->
+ Pid ! {gen_event_EXIT,handler(Handler),Reason},
+ ok
+ end.
+
+report_error(_Handler, normal, _, _, _) -> ok;
+report_error(_Handler, shutdown, _, _, _) -> ok;
+report_error(_Handler, {swapped,_,_}, _, _, _) -> ok;
+report_error(Handler, Reason, State, LastIn, SName) ->
+ Reason1 =
+ case Reason of
+ {'EXIT',{undef,[{M,F,A}|MFAs]}} ->
+ case code:is_loaded(M) of
+ false ->
+ {'module could not be loaded',[{M,F,A}|MFAs]};
+ _ ->
+ case erlang:function_exported(M, F, length(A)) of
+ true ->
+ {undef,[{M,F,A}|MFAs]};
+ false ->
+ {'function not exported',[{M,F,A}|MFAs]}
+ end
+ end;
+ {'EXIT',Why} ->
+ Why;
+ _ ->
+ Reason
+ end,
+ error_msg("** gen_event handler ~p crashed.~n"
+ "** Was installed in ~p~n"
+ "** Last event was: ~p~n"
+ "** When handler state == ~p~n"
+ "** Reason == ~p~n",
+ [handler(Handler),SName,LastIn,State,Reason1]).
+
+handler(Handler) when not Handler#handler.id ->
+ Handler#handler.module;
+handler(Handler) ->
+ {Handler#handler.module, Handler#handler.id}.
+
+the_handlers(MSL) ->
+ [handler(Handler) || Handler <- MSL].
+
+%% stop_handlers(MSL, ServerName) -> []
+
+stop_handlers([Handler|T], SName) ->
+ Mod = Handler#handler.module,
+ do_terminate(Mod, Handler, stop, Handler#handler.state,
+ stop, SName, shutdown),
+ stop_handlers(T, SName);
+stop_handlers([], _) ->
+ [].
+
+%% Message from the release_handler.
+%% The list of modules got to be a set !
+get_modules(MSL) ->
+ Mods = [Handler#handler.module || Handler <- MSL],
+ ordsets:to_list(ordsets:from_list(Mods)).
+
+%%-----------------------------------------------------------------
+%% Status information
+%%-----------------------------------------------------------------
+format_status(_Opt, StatusData) ->
+ [_PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData,
+ Header = lists:concat(["Status for event handler ", ServerName]),
+ [{header, Header},
+ {data, [{"Status", SysState},
+ {"Parent", Parent}]},
+ {items, {"Installed handlers", MSL}}].