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} | Pid
Name = 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} | Pid
Name = 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} | Pid
Name = 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} | Pid
Name = 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} | Pid
Name = 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} | Pid
Name = 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} | Pid
Name = 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} | Pid
Name = atom()
Node = atom()
Pid = pid()
Handler = Module | {Module, Id}
Module = atom()
Id = term()
Query = term()
Timeout = int() > 0 | infinity
Ret = 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} | Pid
Name = 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} | Other
State = 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 | Other
Args1 = 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} | Other
Reply = 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 | Other
Args1 = 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.
Msg
EventMgr ! 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)