NAME
Event::Lib - Perl extentions for event-based programming
SYNOPSIS
use Event::Lib;
use POSIX;
my $seconds;
sub timer {
my $event = shift;
print "\r", ++$seconds;
$event->add(1);
}
sub reader {
my $event = shift;
my $fh = $event->fh;
print <$fh>;
$event->add;
}
sub signal {
my $event = shift;
print "Caught SIGINT\n";
}
my $timer = timer_new(\&timer);
my $reader = event_new(\*STDIN, EV_READ, \&reader);
my $signal = signal_new(SIGINT, \&signal);
$timer->add(1); # triggered every second
$reader->add;
$signal->add;
event_dispatch();
DESCRIPTION
This module is a Perl wrapper around libevent(3) as available from http://www.monkey.org/~provos/libevent/. It allows to execute a function whenever a given event on a filehandle happens, a timeout occurs or a signal is received.
Under the hood, one of the available mechanisms for asynchronously dealing with events is used. This could be select
, poll
, epoll
, devpoll
or kqeue
. The idea is that you don't have to worry about those details and the various interfaces they offer. Event::Lib offers a unified interface to all of them (but see "CONFIGURATION" further below).
Once you've skimmed through the next two sections (or maybe even now), you should have a look at "EXAMPLE: A SIMPLE TCP SERVER" to get a feeling about how it all fits together.
There's also a section briefly mentioning other event modules on the CPAN and how they differ from Event::Lib further below ("OTHER EVENT MODULES").
INITIALIZATION
Most of the time you don't have to do anything other than
use Event::Lib;
However, when you spawn off new processes with fork
and you intend to register and schedule events inside those child processes, you must call event_init
in the spawned processes before you do anything event-related:
use Event::Lib;
my $pid = fork;
if ($pid) {
# parent
wait;
} else {
# I am the child and you have to re-initialize
event_init();
...
}
The reason for that is that the kqueue(2) mechanism doesn't inherit its queue handles to its children.
EVENTS
The standard procedure is to create a few events and afterwards enter a loop (using event_dispatch
) to wait for and handle the pending events.
Event::Lib knows three different kind of events: a filehandle becomes readable/writeable, timeouts and signals.
Watching filehandles
Most often you will have a set of filehandles that you want to watch and handle simultaneously. Think of a webserver handling multiple client requests. Such an event is created with event_new
:
event_new( $fh, $flags, $function, [@args] )
$fh is the filehandle you want to watch. $flags may be the bit-wise ORing of EV_READ, EV_WRITE and EV_PERSIST. EV_PERSIST will make the event persistent, that is: Once the event is triggered, it is not removed from the event-loop. If you do not pass this flag, you have to re-schedule the event in the event-handler $function.
$function is the callback that is executed when the given event happened. This function is always called with at least two arguments, namely the event object itself which was created by the above
event_new
and an integer being the event-type that occured (which could be EV_WRITE, EV_READ or EV_TIMEOUT). @args is an optional list of additional arguments your callback will receive.The function returns an event object (the very object that is later passed to the callback function).
Here's an example how to create a listening socket that can accept connections from multiple clients:
use IO::Socket::INET; sub accept_connection { my $event = shift; my $sock = $event->fh; my $client = $sock->accept; ... } my $server = IO::Socket::INET->new( LocalAddr => 'localhost', LocalPort => 9000, Proto => 'tcp', ReuseAddr => SO_REUSEADDR, Listen => 1, Blocking => 0, ) or die $!; my $main = event_new($server, EV_READ|EV_PERSIST, \&accept_connection); # add the event to the event loop $main->add; event_dispatch();
The above can be done without the EV_PERSIST flag as well:
sub accept_connection { my $event = shift; my $sock = $event->fh; my $client = $sock->accept; ... # re-schedule event $event->add; } ... my $main = event_new($server, EV_READ, \&accept_connection); $main->add; event_dispatch();
event_add( $event, [$timeout] )
$event->add( [$timeout] )
This adds the event previously created with
event_new
to the event-loop. $timeout is an optional argument specifying a timeout given as floating-point number. It means that the event handler is triggered either when the event happens or when $timeout seconds have passed, whichever comes first.$event->fh
Returns the filehandle this $event is supposed to watch. You will usually call this in the event-handler.
event_del( $event )
$event->del
This removes an event object from the event-loop. Note that the object itself is not destroyed and freed. It is merely disabled and you can later re-enable it by calling
$event->add
.event_free( $event )
$event->free
This destroys $event and frees all memory associated with it. After calling this function/method, $event is no longer an object and hence no longer usable.
It will also remove the event from the event-loop if it is still in the event-queue. It is ok to use this function after an event has been deleted with
event_delete
.
Timer-based events
Sometimes you want events to happen periodically, irregardless of any filehandles. Such events are created with timer_new
:
timer_new( $function, [@args] )
This is very much the same as
event_new
, only that it lacks its first two parameters. $function is a reference to a Perl function that should be executed. As always, this function will receive the event object as returned bytimer_new
as first argument, the type of event (always EV_TIMEOUT) plus the optional argumentlist @args.event_add( $event, [$timeout] )
$event->add( [$timeout] )
Adds $event to the event-loop. The event is scheduled to be triggered every $timeout seconds where $timeout can be any floating-point value. If $timeout is omitted, a value of one second is assumed.
Note that timer-based events are not persistent so you have to call this method/function again in the event-handler in order to re-schedule it.
event_del( $event )
$event->del
This removes the timer-event $event from the event-loop. Again, $event remains intact and may later be re-scheduled with
event_add
.event_free( $event )
$event->free
This removes $event from the event-loop and frees any memory associated with it. $event will no longer be usable after calling this method/function.
Signal-based events
Your program can also respond to signals sent to it by other applications. To handle signals, you create the corresponding event using signal_new
.
Note that thusly created events take precedence over event-handlers defined in %SIG
. That means the function you assigned to $SIG{ $SIGNAME }
will never be executed if a Event::Lib
-handler for $SIGNAME
also exists.
signal_new( $signal, $function, [@args] )
Sets up $function as a handler for $signal. $signal has to be an integer specifying which signal to intercept and handle. For example,
15
isSIGTERM
(on most platforms, anyway). You are advised to use the symbolic names as exported by the POSIX module:use Event::Lib; use POSIX; my $signal = signal_new(SIGINT, sub { print "Someone hit ctrl-c" }); $signal->add; event_dispatch();
As always, $function receives the event object as first argument, the event-type (always EV_SIGNAL) as second. @args specifies an option list of values that is to be passed to the handler.
event_add( $event, [$timeout] )
$event->add( [$timeout] )
Adds the signal-event previously created with
signal_new
to the event-loop. $timeout is an optional argument specifying a timeout given as floating-point number. It means that the event handler is triggered either when the event happens or when $timeout seconds have passed, whichever comes first.Note that signal-events are always persistent unless $timeout was given. That means that you have to delete the event manually if you want it to happen only once:
sub sigint { my $event = shift; print "Someone hit ctrl-c"; $event->free; # or maybe: $event->del } my $signal = signal_new(SIGINT, \&sigint); $signal->add; event_dispatch();
Subsequently, a persistent and timeouted signal-handler would read thusly:
sub sigint { my $event = shift; print "Someone hit ctrl-c"; $event->add(2.5); } my $signal = signal_new(SIGINT, \&sigint); $signal->add(2.5); event_dispatch();
event_del( $event )
$event->del
$event->free
These do the same as their counterparts for filehandle- and timer-events (see above).
Common methods
There's one methode that behaves identically for each type of event:
$event->pending
This will tell you whether $event is still in the event-queue waiting to be processed. More specifically, it returns a false value if $event was already handled (and was not either persistent or re-scheduled). In case $event is still in the queue it returns the amount of seconds as a floating-point number until it is triggered again. If $event has no attached timeout, it returns
0 but true
.
ENTERING THE EVENT-LOOP
Event::Lib offers exactly one function that is used to start the main-loop:
event_dispatch( [$flags], [$timeout] )
When called with no arguments at all, this will start the event-loop and never return. More precisely, it will return only if there was an error.
$flags may be one of
EVLOOP_ONCE
andEVLOOP_NONBLOCK
.EVLOOP_ONCE
will make your program enter the main-loop and block until an event happens. In this case, the associated event-handler is called and the function returns.EVLOOP_NONBLOCK
is just likeEVLOOP_ONCE
only that it wont block, that is: It will return immediately if no events are pending.If $timeout is given, the $flags argument is ignored. Instead, your program will enter the main-loop and block for $timeout seconds, handling any events occuring during that time. After the $timeout seconds have passed, it will return.
event_dispatch
can also be invoked as thedispatch
method on any event that you previously created. It will however always affect the whole program.
CONFIGURATION
Event::Lib can be told which kernel notification method not to use:
use Event::Lib qw/no_devpoll no_poll/;
This disables devpoll
and poll
so it will use one of the remaining methods, which could be either select
, epoll
or kqueue
. If you disable all of the available methods, it is a fatal error and you'll receive the message event_init: no event mechanism available
. The available import flags are no_poll
, no_select
, no_epoll
, no_devpoll
and no_kqueue
.
If you want to find out which method Event::Lib internally uses, you can do
use Event::Lib qw/show_method/;
and it will emit something like libevent using: poll
or so.
EXAMPLE: A SIMPLE TCP SERVER
Here's a reasonably complete example how to use this library to create a simple TCP server serving many clients at once. It makes use of all three kinds of events:
use POSIX;
use IO::Socket::INET;
use Event::Lib;
$| = 1;
# Invoked when a new client connects to us
sub handle_incoming {
my $e = shift;
my $h = $e->fh;
my $client = $h->accept or die "Should not happen";
$client->blocking(0);
# set up a new event that watches the client socket
my $event = event_new($client, EV_READ|EV_PERSIST, \&handle_client);
$event->add;
}
# Invoked when the client's socket becomes readable
sub handle_client {
my $e = shift;
my $h = $e->fh;
printf "Handling %s:%s\n", $h->peerhost, $h->peerport;
while (<$h>) {
print "\t$_";
if (/^quit$/) {
# this client says goodbye
close $h;
$e->free;
last;
}
}
}
# This just prints the number of
# seconds elapsed
my $secs;
sub show_time {
my $e = shift;
print "\r", $secs++;
$e->add;
}
# Do something when receiving SIGHUP
sub sighup {
my $e = shift;
# a common thing to do would be
# re-reading a config-file or so
...
}
# Create a listening socket
my $server = IO::Socket::INET->new(
LocalAddr => 'localhost',
LocalPort => 9000,
Proto => 'tcp',
ReuseAddr => SO_REUSEADDR,
Listen => 1,
Blocking => 0,
) or die $!;
my $main = event_new($server, EV_READ|EV_PERSIST, \&handle_incoming);
my $timer = timer_new(\&show_time);
my $hup = signal_new(SIGHUP, \&sighup);
$_->add for $main, $timer, $hup;
$main->dispatch;
__END__
You can test the above server with this little program of which you can start a few several simultaneous instances:
use IO::Socket::INET;
my $server = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => 'localhost',
PeerPort => 9000,
);
print $server "HI!\n";
sleep 10;
print $server "quit\n";
__END__
OTHER EVENT MODULES
There are already a handful of similar modules on the CPAN. The two most prominent ones are Event and the venerable POE framework.
Event
In its functionality it's quite close to Event::Lib with some additional features not present in this module (you can watch variables, for example). Furthermore, you can assign priorities to your events. Interface-wise, it's quite a bit heavier while Event::Lib gets away with just a handful of functions and methods.
The one main advantage of Event::Lib appears to be in its innards. The underlying libevent is capable of employing not just the poll
and select
notification mechanisms but also other and possibly better performing ones such as kqeue
, devpoll
and epoll
where available.
POE
POE is definitely more than the above. It's really a threading environment in disguise. Purely event-based techniques have limitations, most notably that an event-handler blocks all other pending events until it is done with its work. It's therefore not possible to write a parallel link-checker only with Event or Event::Lib. You still need threads or fork(2)
for that.
That's where POE enters the scene. It is truely capable of running jobs in parallel. Such jobs are usually encapsulated in POE::Component
objects of which already quite a few premade ones exist on the CPAN.
This power comes at a price. POE has a somewhat steep learning-curve and forces you to think in POE concepts. For medium- and large-sized applications, this doesn't have to be a bad thing. Once grokked, it's easy to add more components to your project, so it's almost infinitely extensible.
Conclusion
Use the right tools for your job. Event::Lib and Event are good for writing servers that serve many clients at once, or in general: Anything that requires you to watch resources and do some work when something interesting happens with those resources. Once the work needed to be carried out per event gets too complex, you may still use fork
.
Or you use POE. You get the watching and notifying capabilities alright, but also the power to do things in parallel without creating threads or child processes manually.
EXPORT
This modules exports by default the following functions:
event_init
event_new
timer_new
signal_new
event_add
event_del
event_dispatch
plus the following constants:
EVBUFFER_EOF
EVBUFFER_ERROR
EVBUFFER_READ
EVBUFFER_TIMEOUT
EVBUFFER_WRITE
EVLIST_ACTIVE
EVLIST_ALL
EVLIST_INIT
EVLIST_INSERTED
EVLIST_INTERNAL
EVLIST_SIGNAL
EVLIST_TIMEOUT
EVLOOP_NONBLOCK
EVLOOP_ONCE
EV_PERSIST
EV_READ
EV_SIGNAL
EV_TIMEOUT
EV_WRITE
BUGS
Maybe.
This library is almost certainly not thread-safe.
You must include the module either via use
or require
after which you call import
. Merely doing
require Event::Lib;
doesn't work because this module has to load its dynamic portion in the import
method. So if you need to include it at runtine, this will work:
require Event::Lib;
Event::Lib->import;
TO-DO
Not all of libevent's public interface is implemented. The buffered events are still missing. They will be added once I grok what they are for.
Same is true for the two mysterious static variables event_sigcb
and event_gotsig
.
SEE ALSO
libevent's home can be found at http://www.monkey.org/~provos/libevent/. It contains further references to event-based techniques.
Also the manpage of event(3). Note however that Event::Lib functions do not always call their apparent libevent-counterparts. For instance, Event::Lib::event_dispatch
is actually using a combination of int event_loop(...)
and int event_loopexit(...)
to do its work.
VERSION
This is version 0.04.
AUTHOR
Tassilo von Parseval, <tassilo.von.parseval@rwth-aachen.de>
COPYRIGHT AND LICENSE
Copyright (C) 2004 by Tassilo von Parseval
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.4 or, at your option, any later version of Perl 5 you may have available.