toro: Synchronization primitives for Tornado coroutines


Toro logo by Musho Rodney Alan Greenblat

With Tornado’s gen module, you can turn Python generators into full-featured coroutines, but coordination among these coroutines is difficult without mutexes, semaphores, and queues.

Toro provides to Tornado coroutines a set of locking primitives and queues analogous to those that Gevent provides to Greenlets, or that the standard library provides to threads.

(Note that these primitives and queues are not actually thread-safe and cannot be used in place of those from the standard library–they are meant to coordinate Tornado coroutines in single-threaded apps, not to protect shared objects in multithreaded apps.)

The Wait / Notify Pattern

Toro’s primitives follow a “wait / notify pattern”: one coroutine waits to be notified by another. Let’s take Condition as an example:

>>> import toro
>>> from tornado import ioloop, gen
>>> loop = ioloop.IOLoop.current()
>>> condition = toro.Condition()
>>> @gen.coroutine
... def waiter():
...     print "I'll wait right here"
...     yield condition.wait()  # Yield a Future
...     print "I'm done waiting"
>>> @gen.coroutine
... def notifier():
...     print "About to notify"
...     condition.notify()
...     print "Done notifying"
>>> @gen.coroutine
... def runner():
...     # Yield two Futures; wait for waiter() and notifier() to finish
...     yield [waiter(), notifier()]
...     loop.stop()
>>> future = runner(); loop.start()
I'll wait right here
About to notify
Done notifying
I'm done waiting

Wait-methods take an optional deadline argument, which is either an absolute timestamp:

loop = ioloop.IOLoop.current()

# Wait up to 1 second for a notification
yield condition.wait(deadline=loop.time() + 1)

...or a datetime.timedelta for a deadline relative to the current time:

# Wait up to 1 second
yield condition.wait(deadline=datetime.timedelta(seconds=1))

If there’s no notification before the deadline, the Toro-specific Timeout exception is raised.

The Get / Put Pattern

Queue and its subclasses support methods Queue.get() and Queue.put(). These methods are each both a wait-method and a notify-method:

  • Queue.get() waits until there is an available item in the queue, and may notify a coroutine waiting to put an item.
  • Queue.put() waits until the queue has a free slot, and may notify a coroutine waiting to get an item.

Queue.get() and Queue.put() accept deadlines and raise Timeout if the deadline passes.

See the Producer-consumer example.

Additionally, JoinableQueue supports the wait-method JoinableQueue.join() and the notify-method JoinableQueue.task_done().


Is on GitHub:

Bug Reports and Feature Requests

Also on GitHub:

Indices and tables