[Python-ideas] async: feedback on EventLoop API

Geert Jansen geertj at gmail.com
Mon Dec 17 12:08:27 CET 2012


Hi,

below is some feedback on the EventLoop API as implemented in tulip.
I am interested in this for an (alternate) dbus interface that I've written
for Python that supports evented IO. I'm hoping tulip's EventLoop could be an
abstraction as well as a default implementation that allows me to support
just one event interface.

I looked at it from two angles:

 1. Does EventLoop provide everything that is needed from a library writer
    point of view?
 2. Can EventLoop efficiently expose a subset of the functionality of
    some of the main event loop implementations out there today
    (i looked at libuv, libev and Qt).

First some code pointers...

 * https://github.com/geertj/looping - Here i've implemented the EventLoop
   interface for libuv, libev and Qt. It includes a slightly modified version of
   tulip's "polling.py" where I've implemented some of the suggestions below.
   It also adds support for Python 2.6/2.7 as the Python Qt interface (PySide)
   doesn't support Python 3 yet.

 * https://github.com/geertj/python-dbusx - A Python interface for libdbus that
   supports evented IO using an EventLoop interface. This module is also
   tests all the different loops from "looping" by doing D-BUS tests with them
   (looping itself doesn't have tests yet).

My main points of feedback are below:

* It would be nice to have repeatable timers. Repeatable timers are expected
  for example by libdbus when integrating it with an event loop.

  Without repeatable timers, I could emulate a repeatable timer by using
  call_later() and adding a new timer every time the timer fires. This would
  be an inefficient interface though for event loops that natively support
  repeatable timers.

  This could possibly be done by adding a "repeat" argument to call_later().

* It would be nice to be a way to call a callback once per loop iteration.
  An example here is dispatching in libdbus. The easiest way to do this is
  to call dbus_connection_dispatch() every iteration of the loop (a more
  complicated way exists to get notifications when the dispatch status
  changes, but it is edge triggered and difficult to get right).

  This could possibly be implemented by adding a "repeat" argument to
  call_soon().

* A useful semantic for run_once() would be to run the callbacks for
  readers and writers in the same iteration as when the FD got ready.

  This allows for the idiom below when expecting a single event to happen
  on a file descriptor from outside the event loop:

    # handle_read() sets the "ready" flag
    loop.add_reader(fd, handle_read)
    while not ready:
        loop.run_once()

  I use this idiom for example in a blocking method_call() method that calls
  into a D-BUS method.

  Currently, the handle_read() callback would be called in the iteration
  *after* the FD became readable. So this would not work, unless some more
  IO becomes available.

  As far as I can see libev, libuv and Qt all work like this.

* If remove_reader() / remove_writer() would accept the DelayedCall instance
  returned by their add_xxx() cousins, then that would allow for multiple
  callbacks per FD. Not all event loops support this (libuv doesn't, libev
  and Qt do), but for the ones that do could have their functionality could
  be exposed like this. For event loops that don't support this, an exception
  could be raised when adding multiple callbacks per FD.

  Support for multiple callbacks per FD could be advertised as a capability.

* After a DelayedCall is cancelled, it would also be very useful to have a
  second method to enable it again. Having that functionality is more
  efficient than creating a new event. For example, the D-BUS event loop
  integration API has specific methods for toggling events on and off that
  you need to provide.

* (Nitpick) Multiplexing absolute and relative timeouts for the "when"
  argument in call_later() is a little too smart in my view and can lead
  to bugs.

With some input, I'd be happy to produce patches.

Regards,
Geert Jansen



More information about the Python-ideas mailing list