gen_event provides a general framework for building
application specific event handling routines. Event managers can
be built for tasks like:
All event handlers are written as generic event managers and share a common set of interface functions. The generic parts of the event manager contains functions for debugging, handling the termination of the parent, and error handling.
The idea is that a server, the event manager, implements all
server specific parts, while event handlers are added in order
to handle specific events. Each event handler should be
implemented in a module (called the callback module). Each
callback module contains callback functions (e.g.
handle_event/2) which are called whenever the event
manager receives a corresponding message.
Event handlers can be written which act on all events, on some of the events, or on some particular combination of events. Event handlers can also be manipulated at runtime. In particular, an event handler can be:
We can even install several event handlers in the same event manager.
The relationship between the generic interface functions (and received messages) and the callback functions can be illustrated as follows:
Callback module gen_event
---------------- ---------
gen_event:add_handler ----->
Module:init/1 <-----
gen_event:notify ----->
Module:handle_event/2 <-----
gen_event:call ----->
Module:handle_call/2 <-----
gen_event:delete_handler ----->
Module:terminate/2 <-----
gen_event:stop ----->
Module:terminate/2 <-----
gen_event:swap_handler ----->
Mod1:terminate/2 <-----
Mod2:init/1 <-----
Module:handle_info/2 <----- other message
received.
The event manager can be debugged using the sys module.
start() -> ServerRet
start(Name) -> ServerRet
start_link() -> ServerRet
start_link(Name) -> ServerRet
Name = {local, atom()} | {global, atom()}ServerRet = {ok, Pid} | {error, Reason}Pid = pid()Reason = {already_started, Pid} | term()This function starts an event manager. If the manager is
started without Name, it can only be called by using
the returned Pid identifier. If started with
Name, the name is registered locally or globally.
An event manager started with start/0 or
start/1 does not care about the parent. This means
that the parent is not handled explicitly in the generic
manager part. If started in this manner, these functions
must not be used if the event manager is a worker
in a supervision tree.
A manager started with start_link/0 or
start_link/1 is initially linked to the caller - the
parent - and it will terminate whenever the parent process
terminates, with the same reason as the parent. An event
manager always traps exit signals, so the terminate/2
callback function is called for each added event handler in
order to clean up before termination. If started in this
manner, these functions should be used if the event
manager is a worker in a supervision tree.
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()Terminates the event manager. The terminate/2
callback function is called for each added event handler in
order to clean up. The Arg argument of each
terminate/2 will have the value stop.
notify(EventMgr, Event) -> ok
sync_notify(EventMgr, Event) -> ok
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()Event = term()Sends an event notification to the EventMgr event
manager. The Event sent can be any Erlang
term. However, the added event handlers must know about the
term, and for this reason an event format must be specified
for each event manager.
The event manager calls each
associated handle_event/2 callback function to inform
each added event handler about the event.
The
notify/2 function is asynchronous, whereas
sync_notify/2 is synchronous in the sense that it
returns when all handlers have handled the Event.
add_handler(EventMgr, Handler, Args) -> ok | ErrorRet
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()Handler = Module | {Module, Id}Module = atom()Id = term()Args = term()ErrorRet = term()This function adds a new event handler to the
EventMgr event manager. The callback module of the
event handler is Module and the name of the handler
is Handler. The Id term is used to identify a
specific handler when installing several handlers which all
use the same callback module. Args is supplied with
the Module:init(Args) call in order to initialize the
event handler. ErrorRet is any unexpected return
value from the init/1 function.
add_sup_handler(EventMgr, Handler, Args) -> ok |
ErrorRet
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()Handler = Module | {Module, Id}Module = atom()Id = term()Args = term()ErrorRet = term()Adds a new supervised event handler to the EventMgr
event manager. The handler is added in the manner previously
described for the add_handler/3 function.
Whenever the process which evaluated this function
terminates, the Handler is automatically deleted from
the EventMgr. The Module:terminate/2 function
is called in order to clean up with Arg equal to
{stop, Reason}. Reason is the termination
reason of the process.
Whenever the Handler is deleted from the
EventMgr, the process which evaluated this function
receives the message {gen_event_EXIT, Handler,
Reason}. Reason is one of the following:
normal. The handler has been removed by the
delete_handler/3 function, or remove_handler
has been returned by a callback function (see below).
shutdown. The EventMgr process
terminates, or the parent process of the handler terminates
(the parent process could have sent an explicit EXIT
signal to the EventMgr process and expects a message
in response). {swapped, NewHandler,
NewParent}. The handler has been replaced by
NewHandler (see below). Error. The
handler crashed due to Error. Error is any
Erlang term (term()).
delete_handler(EventMgr, Handler, Args) -> DelRet
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()Handler = Module | {Module, Id}Module = atom()Id = term()Args = term()DelRet = term() | {error, module_not_found}Removes the event handler Handler from the
EventMgr event manager. Args is supplied with
the Module:terminate(Args, ...) call in order to
clean up the handler. Normally, it is preferable if
Args is the atom stop as described for
stop/1.
DelRet can be any Erlang term as
returned from the Module:terminate/2 function. This
value can be used later on as a start argument (Args =
DelRet) in order to restart (re-add) the same event
handler with its old internal state. See also
swap_handler/3 below.
swap_handler(EventMgr, OldHandler, NewHandler) ->
SwRet
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()OldHandler = {Handler1, Args1}NewHandler = {Handler2, Args2}Handler1 = Module1 | {Module1, Id1}Handler2 = Module2 | {Module2, Id2}Module1 = Module2 = atom()Id1 = Id2 = term()Args1 = Args2 = term()SwRet = ok | {error, SwErr}SwErr = term()Removes the Handler1 event handler and installs
the new Handler2 event handler. If appropriate, the
new handler can inherit the internal state of the old
handler.
Module1:terminate(Args1,...) is called
to remove the old handler. The return value of the
terminate/2 function is passed to the new handler as
TermRet below. The new handler is initialized by
calling the Module2:init({Args2,TermRet}) function in
the new callback module. If an error occurs, the return
value of the init/1 function is returned as
SwErr. To ignore the internal state of the old
handler, the TermRet value should be ignored in the
init/1 function of the new handler.
If Handler1 was added as a supervised handler, with
the add_sup_handler/3 function for example, the
Handler2 inherits the same parent. Thus,
Handler2 will be supervised by the same process as
Handler1.
swap_sup_handler(EventMgr, OldHandler, NewHandler) ->
SwRet
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()OldHandler = {Handler1, Args1}NewHandler = {Handler2, Args2}Handler1 = Module1 | {Module1, Id1}Handler2 = Module2 | {Module2, Id2}Module1 = Module2 = atom()Id1 = Id2 = term()Args1 = Args2 = term()SwRet = ok | {error, SwErr}SwErr = term()Removes the Handler1 event handler and installs the
new Handler2 event handler in the same manner
described for the swap_handler/3 function above.
The Handler2 event handler will be supervised by the
process that evaluated this function, in the manner
described for the add_sup_handler/3 function above.
call(EventMgr, Handler, Query) -> Ret
call(EventMgr, Handler, Query, Timeout) -> Ret
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()Handler = Module | {Module, Id}Module = atom()Id = term()Query = term()Timeout = int() > 0 | infinityRet = Reply | {error, ErrCall}Reply = term()ErrCall = bad_module | term()Sends a request to the specified event handler
Handler in the EventMgr event manager.
Query can be any Erlang term, but it must be
recognized by the event handler. To handle the request, the
callback function Module:handle_call/2 is
called. bad_module is returned if the Module
event handler does not exist. Reply is the returned
Reply value of the callback function, while
ErrCall is returned as an error descriptor if the
callback module fails.
Timeout should be set to some reasonable value (in
milliseconds). The special value infinity can be used
if the user has no idea how long the request is supposed to
take. If Timeout is not specified, the default value
is 5000.
If Timeout has an integer value and no response has
been delivered within Timeout milliseconds, then the
client will terminate with reason {timeout, {gen_event,
call, [EventMgr, Handler, Query, Timeout]}}.
which_handlers(EventMgr) -> [Handler]
EventMgr = Name | {Name, Node} | {global, Name} | PidName = atom()Node = atom()Pid = pid()Handler = Module | {Module, Id}Module = atom()Id = term()Asks the EventMgr event manager about active
event handlers. This function returns a list of each added
event handler.
The following functions
should be exported from a gen_event callback module.
Args = term()InitRes = {ok, State} | OtherState = term()Other = term()Whenever a new event handler is added to an event
manager, the init/1 function in the specified
callback module is called in order to initialise the
handler. If the initialization function succeeds, it is
supposed to return the initialized internal State of
the handler. The State is passed to all subsequent
callback function calls to the handler.
The Args argument supplied to the init/1
function is the same argument that is supplied to, for
example, the add_handler/3 function.
Module:handle_event(Event, State) -> EventRet
Event = term()EventRet = {ok, State1} | {swap_handler, Args1, State1,
Handler2, Args2} | remove_handler | OtherArgs1 = Args2 = term()State1 = State = term()Handler2 = Module | {Module, Id}Module = atom()Id = term()Other = term()For each event handler, this function is called by the
event manager whenever the event manager has received an
event. Event is the value sent with the
gen_event:notify/2 function call. (Any other
unmatched messages which are received by the event manager -
such as {'EXIT', Pid, Why} - are processed using
handle_info/2)
Normally, the event handler returns a new state with
{ok, State1} after the event has been processed. The
event handler can also remove itself or swap to another
handler. If the handler is removed (returned
remove_handler), the terminate/2 callback
function is called with remove_handler as the first
argument. The swap procedure is the same as described for
swap_handler/3.
If the handle_event/2 function crashes, or returns
Other, the Module:terminate/2 function is
called in order to clean up (if possible) and the handler is
removed from the event manager. The Arg argument of
Module:terminate/2 is {error,Reason}, where
Reason is {'EXIT',Why} if crashed, or
Other.
Module:handle_call(Query, State) -> CallRet
Query = term()CallRet = {ok, Reply, State1} |
{swap_handler, Reply, Args1, State1, Handler2, Args2} |
{remove_handler, Reply} | OtherReply = term()Args1 = Args2 = term()State1 = State = term()Handler2 = Module | {Module, Id}Module = atom()Id = term()Other = term()Handles a request generated by a call/3 function
call. The request is dedicated to this handler.
Query can be any Erlang term recognized by the event
handler. The type of queries which are handled is a design
issue. Reply is any Erlang term which represents the
reply to the call. Reply is returned by the
call/3 function.
Normally, the event handler returns a new state with
{ok, Reply, State1} after the call has been
processed. The event handler can also decide to remove
itself or to swap to another handler. If the handler should
be removed (returned {remove_handler, Reply}), the
terminate/2 callback function is called with
remove_handler as the first argument. The swap
procedure is the same as described for
swap_handler/3.
If the handle_call/2 function crashes, or returns
Other, the Module:terminate/2 function is
called in order to clean up (if possible) and the handler is
removed from the event manager. The Arg argument of
Module:terminate/2 is {error,Reason}, where
Reason is {'EXIT',Why} if crashed, or
Other.
Module:handle_info(Info, State) -> EventRet
Info = term()EventRet = {ok, State1} |
{swap_handler, Args1, State1, Handler2, Args2} |
remove_handler | OtherArgs1 = Args2 = term()State1 = State = term()Handler2 = Module | {Module, Id}Module = atom()Id = term()Other = term()This callback function handles events other than
notify and call, which are received by the event
manager. Typical events, or messages, which are handled include:
{'EXIT', Pid, Reason}{nodedown, Node}nodedown
message is handled here.
MsgEventMgr ! Msg, are also
handled here.
![]() |
Communication with the event manager should always go through the above interface functions. |
The EventRet value is the same as for handle_event.
Module:terminate(Arg, State) -> TermRet
Arg = stop | remove_handler | {error, term()} | {stop,
term()} | term()TermRet = term()Cleans up the event handler before it is removed from
the event manager. If Arg is stop or
remove_handler, the event handler is supposed to be
removed and no other handler is supposed to take over the
internal state. In this case, TermRet is ignored.
If another handler is taking over the internal state of
this handler, this should be marked with Arg as some
other Erlang term, swap for example. In this case,
the event handler should return the internal state
State, or parts of the state, in a way that is
recognized by the handler which is supposed to take over.
Arg is {error, Error} if a callback function
has crashed or returned something inappropriate.
Error is {'EXIT', Why} if it has crashed.
Arg is {stop, Reason} if the parent of a
supervised event handler has terminated. Reason is
the termination reason for the parent process.
Module:code_change(OldVsn, State, Extra) -> {ok,
NewState}
OldVsn = undefined | term()State = term()Extra = term()NewState = term()This function is called when a code change is performed,
which implies that the internal data structures of the
Module event handler has changed. This function is
supposed to convert the old state to the new one.
OldVsn is the vsn attribute of the old
version of the module. If no such attribute was defined, the
atom undefined is sent. Extra is an optional
term, which is typically defined in the release upgrade
script.
The gen_event behaviour generates the following system events,
which are handled by the sys module:
{in, Msg} when a message is received.
sys(3)