Hi!

I used Event for many, many years, and, while its easily the best event
loop out there, I had a number of issues with it (mostly scalability and
bugs that the not-very-active maintainer doesn't fix, but also performance
problems and specific limitations for my uses).

Recently I set out to write an interface to libevent, but since it had the
same kind of artificial limitations and bugs as Tk (which has about the
worst event loop possible :), I created my own replacement for libevent
called libev, and based my perl module on it (it also beats libevent
hands-down, see http://libev.schmorp.de/bench.html).

The perl interface to libev is called EV, is on CPAN, and is of course
not yet as mature as Event, but is used in production already in some
commercial apps, the deliantra game server (and the underlying libev
library is used in the gnu virtual private ethernet and rxvt-unicode
packages). It supports select, poll, epoll, kqueue (on the few platforms
where it works, i.e. "netbsd only") and solaris ports as backends.

Documentation for the perl module: http://cvs.schmorp.de/EV/README
Documentation for libev library: http://cvs.schmorp.de/libev/ev.html

All this is something I wanted to do for many years, but the effort
required kept me from doing it (originally I wanted to take Event and
improve it, it being the best event loop after all, but this didn't turn
out to be reasonable, for example, changing watcher lifetime semantics
would obvioously not be reasonable).

Here is a partial list of (perceived or real) problems or lacks of
features that EV improves upon over Event:

- watcher creation is very fast (about 25 times(!) faster than with
  Event), making it feasible to create and tear down watchers quickly.

  I measured creating and destroying a timer with EV, AnyEvent+EV, Event
  and Glib, and also compared to just using $ev_timer->set+start (EV_set).

  AnyEvent:  3 wallclock secs ( 3.09 usr +  0.00 sys =  3.09 CPU) @ 323624.60/s 
(n=1000000)
        EV:  0 wallclock secs ( 0.95 usr +  0.00 sys =  0.95 CPU) @ 
1052631.58/s (n=1000000)
    EV_set:  0 wallclock secs ( 0.36 usr +  0.00 sys =  0.36 CPU) @ 
2777777.78/s (n=1000000)
     Event: 40 wallclock secs (39.61 usr +  0.01 sys = 39.62 CPU) @ 25239.78/s 
(n=1000000)
      Glib:  3 wallclock secs ( 3.66 usr +  0.01 sys =  3.67 CPU) @ 272479.56/s 
(n=1000000)

- EV watchers are normal perl objects, no need for explicit destruction
  (this is slightly inconvinient for unrealistically simple programs, but
  much simpler for real-world cases).

- it copes with timejumps, both forward and backward (how well it does
  that depends on the availability of a monotonic clock, but it
  copes even in its absence).

- it has usable "hard realtime" timers (the hard realtime timers in Event
  will go into an almost endless loop on timejumps and other delays and
  make them completely useless, as you have to implement your own
  scheduling anyways to make them usable).

- it separates timers into timers based on wallclock time ("periodics")
  and timers based on real time. The former is good for cron-like timers,
  the latter is good for delays and timeouts. The fact that you cannot do
  either with Event always made me uneasy, and also ruled out Event usage
  in some very important use cases, as freezes due to ntp clock resets are
  unacceptable sometimes.

- all operations are either O(1) or O(log n), not O(n) (n = #watchers)
  or worse as in Event. If you have a lot of timers or I/O watchers this
  makes an extreme difference, as Event walks its timer and I/O list on
  every poll call, while in libev, this operation is O(1).

- It uses *much* less memory per watcher than Event (honestly, I never
  saw that as a problem with Event, but in big programs, it made a very
  noticable difference in ps output).

- there cannot be endless resource overflow (the event loop in Event
  can grow endlessly without ever having a chance to reduce its size,
  freezing the program on certain conditions). This is a real big
  problem with Event, which basically makes its priority system useless.

- the main loop is reentrant w.r.t. coroutines.

- it has support for child process status change events.

- EV is much more parsimonous of system calls.

- events have timestamps (at no additional cost. This is useful for more
  exact scheduling of timeouts for example, or simply to see by how much
  event processing is delayed).

- the design is still open to some discussion and feature requests,
  so feel free to ask for things and I might be able to provide them.

It also has a full-featured C API (which Event also has, but some of the
quirks that made it hard to use are gone), delivering the full libev API
to any extensions.

AnyEvent is fully supported (ok, this is not surprising, being from the
same author), so modules using AnyEvent can take advantage of it without
thinking a second.

I also tried to preserve all the good things about Event (which after all,
was my Event module of choice for almost a decade), and decided that sharing
EV with everybody else might be helpful to some. I hope thats true.

(Some of the good things in Event are purely ingenious, for example, the
check and prepare hooks, which allow easy integration of many C and Perl
libraries into Event, something which I have not yet seen in other event
loop implementations, but which I think are absolutely essential. Event
has a number of those concepts not found elsewhere that make it very
mature and general).

EV is available on CPAN, its homepage is linked from http://libev.schmorp.de.

To give you an impression of how the API looks like, here are some examples
from the manpage:

    # TIMERS

    my $w = EV::timer 2, 0, sub {
       warn "is called after 2s";
    };

    my $w = EV::timer 2, 2, sub {
       warn "is called roughly every 2s (repeat = 2)";
    };

    undef $w; # destroy event watcher again

    my $w = EV::periodic 0, 60, 0, sub {
       warn "is called every minute, on the minute, exactly";
    };

    # IO

    my $w = EV::io *STDIN, EV::READ, sub {
       my ($w, $revents) = @_; # all callbacks receive the watcher and event 
mask
       warn "stdin is readable, you entered: ", <STDIN>;
    };

    # SIGNALS

    my $w = EV::signal ’QUIT’, sub {
       warn "sigquit received\n";
    };

    # CHILD/PID STATUS CHANGES

    my $w = EV::child 666, sub {
       my ($w, $revents) = @_;
       my $status = $w->rstatus;
    };

    # MAINLOOP
    EV::loop;           # loop until EV::unloop is called or all watchers stop
    EV::loop EV::LOOP_ONESHOT;  # block until at least one event could be 
handled
    EV::loop EV::LOOP_NONBLOCK; # try to handle same events, but do not block

And here is a rough example of how to integrate e.g. Net::SNMP, by
registering a prepare (called before blocking) and check (called after
blocking) watcher to create and tear down watchers for the sockets
Net::SNMP uses as well as the timeout it needs:

   our @snmp_watcher;

   our $snmp_prepare = EV::prepare sub {
      # do nothing unless active
      $dispatcher->{_event_queue_h}
         or return;

      # make the dispatcher handle any outstanding stuff
      ... not shown

      # create an IO watcher for each and every socket
      @snmp_watcher = (
         (map { EV::io $_, EV::READ, sub { } }
             keys %{ $dispatcher->{_descriptors} }),

         EV::timer +($event->[Net::SNMP::Dispatcher::_ACTIVE]
                     ? $event->[Net::SNMP::Dispatcher::_TIME] - EV::now : 0),
                    0, sub { },
      );
   };

   our $snmp_check = EV::check sub {
      # destroy all watchers
      @snmp_watcher = ();

      # make the dispatcher handle any new stuff
      ... not shown
   };

Thanks for your consideration.

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      [EMAIL PROTECTED]
      -=====/_/_//_/\_,_/ /_/\_\

Reply via email to