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


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

   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.
