Event::Lib(3)

πŸ“¦
version 1.03 Β· 2 versions
, in fedora 43

🌸 NAME 🌸

okay bestie, welcome to the Event::Lib(3) page β€” straight out of the User Contributed Perl Documentation, section 3, and it is iconic.

Event::Lib β€” Perl extentions for event-based programming πŸ’…

(yes, "extentions", that's the name, we don't fix the name, we love the name, no notes ✨)


πŸ¦‹ 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_mainloop;

πŸ’– DESCRIPTION πŸ’–

so this module is a Perl wrapper around libevent(3) β€” you can grab libevent from http://www.monkey.org/~provos/libevent/. What does it do for you? It lets you run a function whenever a given event on a filehandle happens, or when a timeout occurs, or when a signal is received. chef's kiss. 🫢

under the hood, one of the available mechanisms for asynchronously dealing with events gets used. Which one? Could be select, poll, epoll, devpoll or kqueue. The whole point is that you do not have to worry about those details or the various interfaces they offer β€” Event::Lib gives you one unified interface to all of them. (but go peek at "CONFIGURATION" further below, there's lore there.)

once you've skimmed the next two sections (or honestly, even right now, live your life), you should go look at "EXAMPLE: A SIMPLE TCP SERVER" to get a feeling for how it all fits together. 🧩

there's also a section further below that briefly mentions other event modules on the CPAN and how they differ from Event::Lib β€” that's "OTHER EVENT MODULES". πŸ’…


✨ INITIALIZATION ✨

this happens via loading the module with use() or require():

use Event::Lib;

and that's it. No further work is ever required. 🌈

on top of that, you may use the following two functions to retrieve some info about the underlying libevent. Heads up tho: these functions are neither exported nor exportable, so you have to call them fully package-qualified. πŸ‘‘

  • Event::Lib::get_version() β€” returns the version of libevent this module was compiled against. 🏷️
  • Event::Lib::get_method() β€” returns the kernel notification method used by libevent. It'll be one of select, poll, epoll, devpoll and kqueue. πŸ”

πŸ‘‘ EVENTS πŸ‘‘

the standard procedure, bestie: create a few events, then enter the loop with event_mainloop() and wait for / handle the pending events. That loop is truely global and it's shared even between forked processes. Same deal for the events you register β€” they all get processed by that one same loop, no matter where or how you created them. 🌍

every event has a Perl function attached to it that gets triggered when the event is due. And here's the thing: further event handling is delayed until the currently executing event handler is done. So if you want an event to be handled the instant it becomes imminent, it has to run in its own process, so nothing else can disturb it. This matters especially for timer-based events when you expect them to fire steadily every few seconds. ⏰

one more little thing to be aware of, and it gets everybody: sometimes it may apear that your events aren't triggering at all, because they produce no output β€” despite those precious print() statements you so carefully put in. If you see no output, you're a victim of buffering. 😭 The fix is to turn on autoflushing, so drop

$| = 1;

at the top of your program if nothing shows up on your screen or filehandles. slay. πŸ’…

Event::Lib knows three different kind of events: a filehandle becomes readable/writeable, timeouts, and signals. 🌈


πŸŽ€ Watching filehandles πŸŽ€

most often you'll have a set of filehandles 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 makes the event persistent, that is: once the event is triggered, it is not removed from the event-loop. If you don't pass this flag, you have to re-schedule the event in the event-handler $function yourself. πŸ”

$function is the callback executed when the given event happened. This function is always called with at least two arguments: the event object itself (the one created by the event_new() above) and an integer that's the event-type that occured β€” which could be EV_WRITE, EV_READ or EV_TIMEOUT. @args is an optional list of extra arguments your callback will receive. 🎁

NOTE: $fh really ought to be a socket or a pipe. Regular files can't be handled by at least epoll(2). If, for some reason, you want to put an event on a regular file, you have to make sure a kernel notification method is used that can deal with such file-handles. select(2) and poll(2) are good candidates as they don't have this limitation. So in order to prevent this limitation, you have to do:
BEGIN {
    $ENV{ $_ } = 1 for qw/EVENT_NOEPOLL EVENT_NODEVPOLL EVENT_NOKQUEUE/;
}
use Event::Lib;

See "CONFIGURATION" further below for more details. πŸ“š

The function returns an event object β€” the very object that is later passed to the callback function.

here's an example of how to create a listening socket that can accept connections from multiple clients:

use IO::Socket::INET;
use Event::Lib;

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

and 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_mainloop();
  • $event->add( [$timeout] ) β€” alias: event_add( $event, [$timeout] ). This adds the event previously created with event_new() to the event-loop. $timeout is an optional argument specifying a timeout given as a floating-point number. It means the event handler is triggered either when the event happens or when $timeout seconds have passed β€” whichever comes first. ⏳

consider this snippet:

use Event::Lib;

sub handler {
    my ($ev, $type) = @_;
    if ($type == EV_READ) {
        ...
    }
    elsif ($type == EV_TIMEOUT) {
        ...
    }
}

# wait at most for 1.5 seconds
event_new(\*STDIN, EV_READ, \&handler)->add(1.5);
event_one_loop;

If STDIN becomes readable within 1.5 seconds, handler() will be called with $type set to EV_READ. If nothing happens within these 1.5 seconds, it'll be called with $type set to EV_TIMEOUT. πŸ‘€

When $timeout is 0 it behaves as if no timeout has been given, that is: an infinite timeout is assumed. Any other timeout is taken literally, so 0.0 is not the same! In such a case, the event handler will be called immediately with the event type set to EV_TIMEOUT. 😳

it's a fatal error to add the same event multiple times:
my $e = event_new(...);
$e->add;
$e->add;    # this line will die

When an event couldn't be added for some other reason, the event's exception handler is called. See "EXCEPTION HANDLING" further below on how exceptions raised by event_add() differ from other exceptions. 🚨

  • $event->fh β€” returns the filehandle this $event is supposed to watch. You will usually call this in the event-handler. πŸ”Ž
  • $event->remove β€” 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. πŸ’€

πŸ¦‹ Timer-based events πŸ¦‹

sometimes you want events to happen periodically, regardless 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( [$timeout] ) β€” alias: event_add( $event, [$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. ⏱️

it will throw an exception if adding the given event failed. If you still want your program to keep running, wrap this statement into an eval block:

my $e = event_new(...);
eval {
    $e->add;
} or warn "Adding failed";

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. πŸ”

it's a fatal error to add the same event multiple times:
my $e = timer_new(...);
$e->add;
$e->add;    # this line will die

When an event couldn't be added for some other reason, the event's exception handler is called. See "EXCEPTION HANDLING" further below on how exceptions raised by event_add() differ from other exceptions. 🚨

  • $event->remove β€” this removes the timer-event $event from the event-loop. Again, $event remains intact and may later be re-scheduled with event_add(). πŸ’€

✨ Signal-based events ✨

bestie your program can ALSO clap back at signals that other apps send it 🫢. To handle signals you make the matching event with signal_new().

Heads up: events made this way take precedence over event-handlers you defined in %SIG. Translation: the function you assigned to $SIG{ $SIGNAME } will never run if a Event::Lib-handler for $SIGNAME also exists. iykyk πŸ’…
  • signal_new( $signal, $function, [@args] ) β€” sets up $function as a handler for $signal. $signal has to be an integer saying which signal to intercept and handle. Like, 15 is SIGTERM (on most platforms, anyway πŸ™ˆ). You're advised to use the symbolic names as exported by the POSIX module:
use Event::Lib;
use POSIX qw/SIGINT/;

my $signal = signal_new(SIGINT, sub { print "Someone hit ctrl-c" });
$signal->add;
event_mainloop();

As always (the way that this is literally always the rule), $function receives the event object as first argument, and the event-type (always EV_SIGNAL) as second. @args is an option list of values that gets passed to the handler.

  • $event->add( [$timeout] ) β€” alias: event_add( $event, [$timeout] ).

Adds the signal-event you previously created with signal_new() to the event-loop. $timeout is optional and it's a timeout given as a floating-point number. What it means: the event handler fires either when the event happens or when $timeout seconds have passed β€” whichever comes first, no cap ⏰.

$timeout here has the exact same semantics as with filehandle-based events described further above. (Go look, bestie, it's all there.)

Note that signal-events are always persistent unless $timeout was given. That means 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->remove;
}

my $signal = signal_new(SIGINT, \&sigint);
$signal->add;
event_mainloop();

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

It's a fatal error to add the same event multiple times:

my $e = signal_new(...);
$e->add;
$e->add;    # this line will die

When an event couldn't be added for some other reason, the event's exception handler gets called instead. See "EXCEPTION HANDLING" further below on how exceptions raised by event_add() differ from other exceptions. πŸ‘€

  • $event->remove β€” the same as their counterparts for filehandle-events, so please see above. (Yes, same energy, same behaviour, go read it πŸ’–)

✨ COMMON METHODS ✨

  • $event->pending β€” this tells 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). If $event is still in the queue, it returns the amount of seconds as a floating-point number until it's triggered again. And if $event has no attached timeout, it returns "0 but true" (iconic little string, honestly πŸ‘‘).
  • $event->args( [@args] ) β€” when called with no arguments, in scalar context it returns the number of additional arguments associated with $event. In list context, it returns those arguments as one list.

When @args is given, the current list of arguments for $event is replaced with @args and nothing is returned. Swap and drop πŸ’…

  • $event->args_del β€” removes all additional arguments from $event, so the next time the event handler is called, the list of additional arguments passed to it will be empty. Clean slate energy ✨
  • $event->callback β€” returns the callback associated with this event as a code-reference, so you can call it manually in case you think you need that:
$event->callback->($event, $event->fh, @args);
  • $event->except_handler( $function ) β€” you can associate an exception handler with each event, and it gets called in case the callback for that event dies 😭. $function is a Perl code-reference which β€” when called β€” will receive the event as first argument, the error message with which the event handler died, the type of event, and any additional arguments associated with that event. That way you can inspect the circumstances and provide your own error-handling.

Please see "EXCEPTION HANDLING" for some background and more details.


🌈 ENTERING THE EVENT-LOOP 🌈

Event::Lib offers three functions to process pending events.

  • event_mainloop () β€” this function starts the event-loop and never returns, generally. More precisely: it returns if either the program ran out of events, in which case event_mainloop() returns a true value. In case of an error during event-processing, it returns a false value β€” in which case you should check $!.
IMPORTANT: when any of your events register new events, they get added to the global queue of events and get handled in the same loop. You are therefore not allowed to call event_mainloop() more than once in your program. Attempting to do so will yield a warning, and the operation is silently turned into a no-op. (Silently!! The audacity πŸ’€)
  • event_one_loop( [$timeout] ) β€” this does exactly one loop, which means the next pending event is handled. In case no event is currently ready for processing, it blocks and waits until one becomes processible.

If $timeout is specified, it waits at most $timeout seconds and then returns.

  • event_one_nbloop () β€” the non-blocking counterpart to event_one_loop(): it returns immediately when no event is ready to be processed. Otherwise, the next imminent event is handled.

You want to use either event_one_loop() or event_one_nbloop() instead of event_mainloop() if you want to write your own event-loop. The core of such a program could look like this:

event_new(...)->add;
event_new(...)->add;
timer_new(...)->add;
...

while () {
    event_one_nbloop();
    ...
    select undef, undef, undef, 0.05;   # sleep for 0.05 seconds
}

πŸ¦‹ EVENT LIFECYCLE πŸ¦‹

It is important to understand the lifetime of events, because concepts such as scope and visibility have little meaning with respect to events. Sit down for this one πŸ“

When you add an event to the queue using event_add(), this event will remain there until it is triggered β€” no matter what you do with the object returned by event_new(), timer_new() and signal_new() respectively. Consider this code:

use Event::Lib;
$| = 1;

my $event = timer_new(sub { print "timer called\n" });

# schedule the timer to go off in ten seconds
$event->add(10);

undef $event;
event_mainloop;

This program will, regardless of the undef $event, print timer called. As a consequence, there is only one true and correct way to cancel an event, namely by calling remove() on it. Likewise:

use warnings;
use Event::Lib;
$| = 1;

my $event = timer_new(sub { print "Called after two seconds\n" });
$event->add(2);
$event = timer_new(sub { print "Called after three seconds\n" });
$event->add(3);

event_mainloop;

__END__
Explicit undef() of or reassignment to pending event at - line 8.
Called after two seconds
Called after three seconds

So even though you have only one Perl object container $event, you have two events! Two!! Not me finding that unhinged 🫠

As this can become hard to maintain in complex programs, Event::Lib will emmit a warning if any of the above cases is detected and if you have warnings enabled. If you don't want this warning, turn it off temporarily. The above program then becomes:

use warnings;
use Event::Lib;
$| = 1;

my $event = timer_new(sub { print "Called after two seconds\n" });
$event->add(2);

{
    no warnings 'misc';
    $event = timer_new(sub { print "Called after three seconds\n" });
    $event->add(3);
}

event_mainloop;

Note that the line following the undef() or the reassignment has to be within the no warnings 'misc'-block, because this is the line where this warning is actually triggered and not the line with the undef() or reassignment itself. Sneaky, right? πŸ’…

πŸ’– EXCEPTION HANDLING πŸ’–

Okay bestie, some programs simply cannot afford to die. πŸ’€ It is possible that a callback is triggered and finds itself in a situation where it just cannot proceed. Think of a callback that is supposed to append to a file and in the meantime the disk has filled so that no space is left on the device. not the vibe, fr fr.

It is now possible to provide exception handlers for these cases. The idea is that these exception handlers are called with the same arguments the callback was previously triggered with (plus the error message as second argument) which gives you the chance to further investigate the cause of the failure and possibly take counter-measures. 🫢 lowkey genius.

You can register exception handlers per event using the except_handler() method. Furthermore, you can register one global exception handler that is going to be used for all events that don't have their own handler:

  • event_register_except_handler( $function ) β€” $function is a code-reference that will be called whenever the callback of an event dies:
use Event::Lib;

sub handler {
    my ($event, $exception, $type, @args) = @_;
    # error handling here
    ...
}

event_register_except_handler(\&handler);
...

If you don't call event_register_except_handler() Event::Lib will use its own basic default handler. This handler simply dies with the original error message. iconic, honestly.

🚨 Exceptions raised by event_add() 🚨

If the exception was raised by event_add(), then the event's exception handler is called. This is either the one registered with except_handler() on a per-event basis, the global one set via event_register_except_handler() or, if both of these was not done, the default handler. no cap.

In any case, the exception handler called from event_add() is called with slightly different arguments. This is in order to allow the handler to distinguish between the case where an exception was raised by an event-handler or where it was raised by event_add().

The first two arguments being the event in question and the error message are the same for both kind of exceptions. What differs is the third argument, $type. It will always be negative when event_add() triggered this exception. periodt.

In particular, the type of event $type will be for a...

... filehandle-event:

The negated type-flags with which the event was created. This means that for the following exception-handler and when "$e->add" failed:

sub exception_handler {
    my ($e, $err, $evtype, @args) = @_;

    # ref($e) eq "Event::Lib::event"
    # $err =~ /^Couldn't add event at/
    # $evtype == -(EV_READ|EV_PERSIST)
    # @args == (1, 2, 3)
    # $! will contain the OS-level error
}

my $e = event_new(\*FH, EV_READ|EV_PERSIST, \&handler, 1 .. 3);
...
$e->add;

... timer-event:

$type will be -EV_TIMEOUT:

sub exception_handler {
    my ($e, $err, $evtype, @args) = @_;

    # ref($e) eq "Event::Lib::timer"
    # $err =~ /^Couldn't add event at/
    # $evtype == -EV_TIMEOUT
    # @args == (1, 2, 3)
}

my $e = timer_new(\&handler, 1 .. 3);
...
$e->add;

... signal-event:

$type will be the negated signal number this event was supposed to handle:

sub exception_handler {
    my ($e, $err, $evtype, @args);

    # ref($e) eq "Event::Lib::signal"
    # $err =~ /^Couldn't add event at/
    # $evtype == -SIGTERM
    # @args == (1, 2, 3)
}

my $e = signal_new(SIGTERM, \&handler, 1 .. 3);
...
$e->add;

As a consequence, first have your exception-handler test the sign of $evtype. If it was negative, use ref($e) to extract the kind of event. slay.


πŸ‘‘ 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 calling event_mainloop():

  • event_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. ✨

πŸ” FUNCTIONS FOR DEBUGGING, TRACING ET AL. πŸ”

There are some functions that will aid you in finding problems in your program or even to assure you that your program is ok but there might be a bug in Event::Lib. iykyk.

  • event_log_level( $loglevel ) β€” You can specify what kind of messages Event::Lib should dump to stderr by using this function.

$loglevel is one of _EVENT_LOG_DEBUG, _EVENT_LOG_MSG, _EVENT_LOG_WARN, _EVENT_LOG_ERR and _EVENT_LOG_NONE and will instruct Event::Lib to only output messages of at least that severity. "_EVENT_LOG_NONE" will suppress any messages. Not calling this function is equivalent to doing

event_log_level( _EVENT_LOG_ERR );
  • $event->trace β€” This turns on tracing for $event. Tracing means that diagnostic messages are written to STDERR whenever something happens to this $event. This includes implicit action such as the destruction of an event or explicit things like calling add() or remove() or other methods on $event.

Returns $event so that you can easily plug it into your code:

event_new(...)->trace->add;

Once an event is traced, there is as of now no way to untrace it again.

  • Event::Lib::Debug::get_pending_events() β€” This function is only available when you built Event::Lib with "DEFINE=-DEVENT_LIB_DEBUG" (as an argument to perl Makefile.PL). Additionally, you have to run your program with the environment variable "EVENT_LIB_DEBUG_PENDING" set in order to get any output from this function. The environment has to be set before "use Event::Lib;":
BEGIN {
    $ENV{ EVENT_LIB_DEBUG_PENDING } = 1;
}

use Event::Lib;

or by setting it in your shell. For the bash, this looks like:

$ EVENT_LIB_DEBUG_PENDING=1 perl event_script.pl

This function will return a list of all currently still pending events. Each element of this list is a reference to an array, where the first element is the event object, the second the type of event ("EV_TIMEOUT", "EV_SIGNAL", "EV_READ" etc.) and the remaining elements the additional arguments this event was constructed with. obsessed.

  • Event::Lib::Debug::dump_pending_events() β€” Similar to the above, only that it will dump all currently pending events to STDERR with some additional information that might be of interest.

Again, this is only available when the module was build with "-DEVENT_LIB_DEBUG" and with the environment variable "EVENT_LIB_DEBUG_PENDING" set.


βš™οΈ CONFIGURATION βš™οΈ

Event::Lib can be told which kernel notification method not to use. This happens via the use of environment variables (there is no other way due to libevent). They have to be set in a BEGIN-block before you use() Event::Lib:

BEGIN {
    $ENV{ $_ } = 1 for qw/EVENT_NOPOLL EVENT_NOEPOLL/;
}

use Event::Lib;

This will disable "poll" and "epoll" so it will use one of the remaining methods, which could be either "select", "devpoll" or "kqueue". the way that works.

The variables that you may set are the following:

  • EVENT_NOPOLL
  • EVENT_NOSELECT
  • EVENT_NOEPOLL
  • EVENT_NODEVPOLL
  • EVENT_NOKQUEUE

If you set all of the above variables, it is a fatal error and you'll receive the message "event_init: no event mechanism available". There is one other variable available:

  • EVENT_LOG_LEVEL β€” This is the environment-variable version of set_log_level() intended to conveniently run your script more verbosely for debugging purpose. The lower this value is, the more informational output libevent produces on STDERR. "EVENT_LOG_LEVEL=0" means maximum debugging output whereas "EVENT_LOG_LEVEL=4" means no output at all:
$ EVENT_LOG_LEVEL=0 perl your_script.pl

✨ EXAMPLE: A SIMPLE TCP SERVER ✨

okay bestie, THIS is the moment πŸ’– here's a pretty complete example of how you use this library to build a little TCP server that serves a whole crowd of clients at once. And the icon that it is? It uses all three kinds of events. slay.

use POSIX qw/SIGHUP/;
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->remove;
            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;

event_mainloop;

__END__

ngl, to actually test that server you can run this lil baby, and you can even fire up a bunch of instances at the same time:

use IO::Socket::INET;

my $server = IO::Socket::INET->new(
    Proto       => 'tcp',
    PeerAddr    => 'localhost',
    PeerPort    => 9000,
) or die $@;

print $server "HI!\n";
sleep 10;
print $server "quit\n";

__END__

🌈 OTHER EVENT MODULES 🌈

so there's already a lil squad of similar modules up on the CPAN, and the two big names in the group chat are Event and the venerable POE framework. let's gossip about them πŸ’…

πŸ’œ Event

functionality-wise it's pretty close to Event::Lib, except it's got some extra features this module just doesn't have (you can watch variables, for example). interface-wise though? it's lowkey heavier, whereas Event::Lib gets by with literally just a handful of functions and methods. on the flip side, it's been around for years, so you can count on Event being rock-stable. queen has tenure.

the one big flex Event::Lib has is in its innards ✨ the underlying libevent can use not just the "poll" and "select" notification mechanisms but also other, possibly better-performing ones like "kqueue", "devpoll" and "epoll" wherever they're available. iykyk.

πŸ¦‹ POE

POE is definitely more than all of the above. it's really a threading environment in disguise, no cap. pure event-based techniques have limits, and the most notable one is that an event-handler blocks every other pending event until it's done doing its work. which means you straight-up cannot write a parallel link-checker with only Event or Event::Lib. for that you still need threads or fork(2), periodt.

and that's exactly where POE walks in πŸ’… it is truly capable of running jobs in parallel. those jobs are usually wrapped up in "POE::Component" objects, and there are already quite a few premade ones chillin' on the CPAN.

but this power comes at a price, bestie. POE has a somewhat steep learning-curve and it forces you to think in POE concepts. for medium- and large-sized applications that's not necessarily a bad thing though. once you've grokked it, adding more components to your project is easy, so it's almost infinitely extensible. iconic behavior honestly.

🌸 Stem

Stem is a very close rival to POE β€” they're nose-to-nose on features. but Stem's design is a lot easier to understand and to adapt to your needs, mostly because it doesn't come up with its own methodology and terminology. it's very well thought out without being over-designed. we love a humble king 🫢

simple event-looping is easy and straight-forward (it currently ships with its own well-conceived event loop, and on top of that it can make use of Event when that's available). so-called Stem cells can be easily plugged together to build big applications, and those cells can run in parallel, both asynchronously or in a synchronized fashion. cute AND functional.

its main drawback (as of now) is its lack of documentation 😭 however, it's been written in such a clean way that its source can often serve as a drop-in replacement for the missing docs. read the source, bestie.

⭐ Conclusion

use the right tools for your job, periodt. Event::Lib and Event are great for writing servers that serve many clients at once, or in general: anything where you need to watch resources and do some work when something interesting happens with those resources. and once the work needed per event gets too complex, you can still reach for "fork".

or you go with Stem or POE. you get the watching and notifying capabilities, sure, but also the power to do things in parallel without manually creating threads or child processes. main character energy.


πŸŽ€ EXPORT πŸŽ€

this module exports the following functions by default:

event_init
event_log_level
event_priority_init
event_register_except_handler
event_fork

event_new
timer_new
signal_new

event_add

event_mainloop
event_one_loop
event_one_nbloop

plus these constants, bestie:

EV_PERSIST
EV_READ
EV_SIGNAL
EV_TIMEOUT
EV_WRITE
_EVENT_LOG_DEBUG
_EVENT_LOG_MSG
_EVENT_LOG_WARN
_EVENT_LOG_ERR
_EVENT_LOG_NONE

πŸ’€ BUGS πŸ’€

this library is not thread-safe. okay so that's the big one.

the module has turned out to be quite stable under stress-situations β€” handling many thousands of simultaneous connections with a very decent performance, which it owes to the underlying libevent. however, event-based applications can reach a stupendous complexity and it is not possible to foresee every kind of conceivable scenario. πŸ™ˆ

so if you find a bug (a crash, a memory leak, inconsistencies or omissions in this documentation, or just about anything else), don't hesitate to contact me. check out "AUTHOR" further down for the deets.


πŸ”₯ TO-DO πŸ”₯

thread-safety is high on the list. recent libevent has thread-support which will make this fairly easy. we love an easy W.

not all of libevent's public interface is implemented. the buffered events are still missing. they'll get added once i grok what they're for 😭


πŸ’– THANKS πŸ’–

this module wouldn't be in its current state without the patient and professional help of MailChannels Corporation (<http://www.mailchannels.com>). over the course of five months, Stas Bekman, Ken Simpson and Mike Smith exchanged hundreds of emails with me, pointing out the many glitches that were in the module and coming up with test-cases that made it possible for me to fix all these issues. absolute legends, no cap 🫢


πŸ‘€ SEE ALSO πŸ‘€

libevent's home can be found at <http://www.monkey.org/~provos/libevent/>. it's got further references to event-based techniques in there.

also the manpage of event(3).


🌟 VERSION 🌟

this is version 1.03.


πŸ‘‘ AUTHOR πŸ‘‘

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


Copyright (C) 2004-2007 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.


and that's a wrap on the whole page, bestie πŸŽ€πŸ’… this one came from perl v5.42.0 and was dated 2025-07-25 β€” go forth and event-loop responsibly ✨

πŸ“¦ where this page comes from