The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Event::Lib - Perl extentions for event-based programming

SYNOPSIS

    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();

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 by timer_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 is SIGTERM (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).

Priorities

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.

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 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_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.

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
    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

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.

Neither did I yet look into libevent's experimental thread-support. Once the "experimental" flag is removed, I might do that.

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.09.

AUTHOR

Tassilo von Parseval, <tassilo.von.parseval@rwth-aachen.de>

COPYRIGHT AND LICENSE

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.