Event::Lib - Perl extentions for event-based programming
use Event::Lib; use POSIX qw/SIGINT/; 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();
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).
select
poll
epoll
devpoll
kqeue
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").
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:
fork
event_init
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.
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_dispatch
Event::Lib knows three different kind of events: a filehandle becomes readable/writeable, timeouts and signals.
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
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->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.
event_delete
Sometimes you want events to happen periodically, irregardless of any filehandles. Such events are created with timer_new:
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 by timer_new as first argument, the type of event (always EV_TIMEOUT) plus the optional argumentlist @args.
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.
This removes the timer-event $event from the event-loop. Again, $event remains intact and may later be re-scheduled with event_add.
event_add
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.
Your program can also respond to signals sent to it by other applications. To handle signals, you create the corresponding event using signal_new.
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.
%SIG
$SIG{ $SIGNAME }
Event::Lib
$SIGNAME
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 is SIGTERM (on most platforms, anyway). You are advised to use the symbolic names as exported by the POSIX module:
15
SIGTERM
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.
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();
These do the same as their counterparts for filehandle- and timer-events (see above).
Events can be assigned a priority. The lower its assigned priority is, the earlier this event is processed. Using prioritized events in your programs requires two steps. The first one is to set the number of available priorities. Setting those should happen once in your script and before any events are dispatched:
priority_init( $priorities )
Sets the number of different events to $priorities.
Assigning a priority to each event then happens thusly:
$event->set_priority( $priority )
Gives $event (which can be any of the three type of events) the priority $priority. Remember that a lower priority means the event is processed earlier!
Note: If your installed version of libevent does not yet contain priorities which happens for pre-1.0 versions, the above will become no-ops. Other than that, your scripts will remain functional.
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.
0 but true
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 and EVLOOP_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_ONCE
EVLOOP_NONBLOCK
EVLOOP_NONBLOCK is just like EVLOOP_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 the dispatch method on any event that you previously created. It will however always affect the whole program.
dispatch
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.
kqueue
event_init: no event mechanism available
no_poll
no_select
no_epoll
no_devpoll
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.
libevent using: poll
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__
There are already a handful of similar modules on the CPAN. The two most prominent ones are Event and the venerable POE framework.
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 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.
fork(2)
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.
POE::Component
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.
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.
This modules exports by default the following functions:
event_init priority_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
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
use
require
import
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;
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.
event_sigcb
event_gotsig
Neither did I yet look into libevent's experimental thread-support. Once the "experimental" flag is removed, I might do that.
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.
Event::Lib::event_dispatch
int event_loop(...)
int event_loopexit(...)
This is version 0.07.
Tassilo von Parseval, <tassilo.von.parseval@rwth-aachen.de>
Copyright (C) 2004-2005 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.
To install Event::Lib, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Event::Lib
CPAN shell
perl -MCPAN -e shell install Event::Lib
For more information on module installation, please visit the detailed CPAN module installation guide.