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):

**epolls**
   Requires Python 2.6 or the python-epoll package, and Linux.  This
   is the fastest pure-Python hub.

**poll**
   On platforms that support it

**selects**
   Lowest-common-denominator, available everywhere.

**pyevent**
   This is a libevent-based backend and is thus the fastest.  It's
   disabled by default, because it does not support native threads,
   but you can enable it yourself if your use case doesn't require
   them.  (You have to install pyevent, too.)

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 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 """
      from eventlet import hubs
      hubs.use_hub("pyevent")

   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.:

      from eventlet import hubs
      from mypackage import myhub
      hubs.use_hub(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 is why it's not necessary to explicitly call a dispatch()
method like other frameworks, which in turn means that code can start
using Eventlet without needing to be substantially restructured.


More Hub-Related Functions
==========================

eventlet.hubs.get_hub()

   Get the current event hub singleton object.

   Note: This is considered an internal API, and it might change
     unexpectedly without being deprecated first.

eventlet.hubs.get_default_hub()

   Select the default hub implementation based on what multiplexing
   libraries are installed.  The order that the hubs are tried is:

   * epoll

   * kqueue

   * poll

   * select

   It won't automatically select the pyevent hub, because it's not
   python-thread-safe.

   Note: This is considered an internal API, and it might change
     unexpectedly without being deprecated first.

eventlet.hubs.trampoline(fd, read=None, write=None, timeout=None, timeout_exc=<class 'eventlet.timeout.Timeout'>, mark_as_closed=None)

   Suspend the current coroutine until the given socket object or file
   descriptor is ready to *read*, ready to *write*, or the specified
   *timeout* elapses, depending on arguments specified.

   To wait for *fd* to be ready to read, pass *read* "=True"; ready to
   write, pass *write* "=True". To specify a timeout, pass the
   *timeout* argument in seconds.

   If the specified *timeout* elapses before the socket is ready to
   read or write, *timeout_exc* will be raised instead of
   "trampoline()" returning normally.

   Note: This is considered an internal API, and it might change
     unexpectedly without being deprecated first.
