Understanding Eventlet Hubs

A hub forms the basis of Eventlet’s event loop, which dispatches I/O events and schedules greenthreads. It is the existence of the hub that promotes coroutines (which can be tricky to program with) into greenthreads (which are easy).

Eventlet has multiple hub implementations, and when you start using it, it tries to select the best hub implementation for your system. The hubs that it supports are (in order of preference):

asyncio
Asyncio based hub. Run Eventlet code in an Asyncio eventloop.
By using this hub, Asyncio and Eventlet can be run the same thread in the same process.
We discourage new Eventlet projects.
We encourage existing Eventlet projects to migrate from Eventlet to Asyncio.
This hub allow you incremental and smooth migration.
See the Migrating off of Eventlet for further details.
See the Asyncio compatibility in eventlet for the current state of the art.
epolls

Linux. This is the fastest hub for Linux.

kqueue

FreeBSD and Mac OSX. Fastest hub for OS with kqueue.

poll

On platforms that support it.

selects

Lowest-common-denominator, available everywhere.

The only non-pure Python, pyevent hub (using libevent) was removed because it was not maintained. You are warmly welcome to contribute fast hub implementation using Cython, CFFI or other technology of your choice.

If the selected hub is not ideal for the application, another can be selected. You can make the selection either with the environment variable EVENTLET_HUB, or with eventlet.hubs.use_hub().

eventlet.hubs.use_hub(hub=None)

Use this to control which hub Eventlet selects. Call it with the name of the desired hub module. Make sure to do this before the application starts doing any I/O! Calling use_hub completely eliminates the old hub, and any file descriptors or timers that it had been managing will be forgotten. Put the call as one of the first lines in the main module.:

""" This is the main module """
import eventlet.hubs
eventlet.hubs.use_hub("eventlet.hubs.epolls")

Hubs are implemented as thread-local class instances. eventlet.hubs.use_hub() only operates on the current thread. When using multiple threads that each need their own hub, call eventlet.hubs.use_hub() at the beginning of each thread function that needs a specific hub. In practice, it may not be necessary to specify a hub in each thread; it works to use one special hub for the main thread, and let other threads use the default hub; this hybrid hub configuration will work fine.

It is also possible to use a third-party hub module in place of one of the built-in ones. Simply pass the module itself to eventlet.hubs.use_hub(). The task of writing such a hub is a little beyond the scope of this document, it’s probably a good idea to simply inspect the code of the existing hubs to see how they work.:

import eventlet.hubs
import mypackage.myhub
eventlet.hubs.use_hub(mypackage.myhub)

Supplying None as the argument to eventlet.hubs.use_hub() causes it to select the default hub.

How the Hubs Work

The hub has a main greenlet, MAINLOOP. When one of the running coroutines needs to do some I/O, it registers a listener with the hub (so that the hub knows when to wake it up again), and then switches to MAINLOOP (via get_hub().switch()). If there are other coroutines that are ready to run, MAINLOOP switches to them, and when they complete or need to do more I/O, they switch back to the MAINLOOP. In this manner, MAINLOOP ensures that every coroutine gets scheduled when it has some work to do.

MAINLOOP is launched only when the first I/O operation happens, and it is not the same greenlet that __main__ is running in. This lazy launching means that code can start using Eventlet without needing to be substantially restructured.