python-gevent/docs/loop_impls.rst

248 lines
9.2 KiB
ReStructuredText

=============================================
Event Loop Implementations: libuv and libev
=============================================
.. versionadded:: 1.3
gevent offers a choice of two event loop libraries (`libev`_ and
`libuv`_) and three event loop implementations. This document will
explore those implementations and compare them to each other.
Using A Non-Default Loop
========================
First, we will describe how to choose an event loop other than the
default loop for a given platform. This is done by setting the
``GEVENT_LOOP`` environment variable before starting the program, or
by setting :attr:`gevent.config.loop <gevent._config.Config.loop>` in
code.
.. important::
If you choose to configure the loop in Python code, it must be done
*immediately* after importing gevent and before any other gevent
imports or asynchronous operations are done, preferably at the top
of your program, right above monkey-patching (if done)::
import gevent
gevent.config.loop = "libuv"
.. important::
In gevent 1.4 and 1.3, if you install gevent from a manylinux1
binary wheel as distributed on PyPI, you will not be able to use
the libuv loop. You'll need to compile from source to gain access
to libuv. gevent 1.5 distributes manylinux2010 wheels which have
libuv support.
If you use a Linux distribution's package of gevent, you may or may
not have any other loops besides the default.
Loop Implementations
====================
Here we will describe the available loop implementations.
+----------+-------+------------+------------+-----+--------------+---------+--------+
|Name |Library|Default |Interpreters|Age |Implementation|Build |Embedded|
| | | | | | |Status | |
+==========+=======+============+============+=====+==============+=========+========+
|libev |libev |CPython on |CPython only|8 |Cython |Default |Default;|
| | |non-Windows | |years| | |optional|
| | |platforms | | | | | |
+----------+-------+------------+------------+-----+--------------+---------+--------+
|libev-cffi|libev |PyPy on |CPython and |4 |CFFI |Optional;|Default;|
| | |non-Windows |PyPy |years| |default |optional|
| | |platforms | | | | | |
+----------+-------+------------+------------+-----+--------------+---------+--------+
|libuv |libuv |All |CPython and |2 |CFFI |Optional;|Default;|
| | |interpreters|PyPy |years| |default |optional|
| | |on Windows | | | | | |
+----------+-------+------------+------------+-----+--------------+---------+--------+
.. _libev-impl:
libev
-----
`libev`_ is a venerable event loop library that has been the default
in gevent since 1.0a1 in 2011 when it replaced libevent. libev has
existed since 2007.
.. note::
In the future, this Cython implementation may be deprecated to be
replaced with :ref:`libev-cffi`.
.. _libev-dev:
.. rubric:: Development and Source
libev is a stable library and does not change quickly. Changes are
accepted in the form of patches emailed to a mailing list. Due to its
age and its portability requirements, it makes heavy use of
preprocessor macros, which some may find hinders readability of the
source code.
.. _libev-plat:
.. rubric:: Platform Support
gevent tests libev on Linux and macOS. There is no known list of
platforms officially supported by libev, although FreeBSD, OpenBSD and
Solaris/SmartOS have been reported to work with gevent on libev at
various points.
On Windows, libev has `many limitations
<http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#WIN32_PLATFORM_LIMITATIONS_AND_WORKA>`_.
gevent relies on the Microsoft C runtime functions to map from Windows
socket handles to integer file descriptors for libev using a somewhat
complex mapping; this prevents the CFFI implementation from being
used (which in turn prevents PyPy from using libev on Windows).
There is no known public CI infrastructure for libev itself.
.. _libev-cffi:
libev-cffi
----------
This uses libev exactly as above, but instead of using Cython it uses
CFFI. That makes it suitable (and the default) for PyPy. It can also
make it easier to debug, since more details are preserved for
tracebacks.
.. note::
In the future, this CFFI implementation may become the default and replace
:ref:`libev-impl`.
.. rubric:: When To Use
On PyPy or when debugging.
libuv
-----
libuv is an event loop library developed since 2011 for the use of
node 0.5. It was originally a wrapper around libev on non-Windows
platforms and directly used the native Windows IOCP support on Windows
(this code was contributed by Microsoft). Now it has its own loop
implementation on all supported platforms.
libuv provides libev-like `"poll handles"
<http://docs.libuv.org/en/v1.x/poll.html>`_, and in gevent 1.3 that is
what gevent uses for IO. But libuv also provides higher-level
abstractions around read and write requests that may offer improved
performance. In the future, gevent might use those abstractions.
.. note::
In the future, this implementation may become the default on all
platforms.
.. rubric:: Development and Source
libuv is developed by the libuv organization on `github
<https://github.com/libuv/libuv>`_. It has a large, active community
and is used in many popular projects including node.js.
The source code is written in a clean and consistent coding style,
potentially making it easier to read and debug.
.. rubric:: Platform Support
gevent tests libuv on Linux, Windows and macOS. libuv publishes an
extensive list of `supported platforms
<https://github.com/libuv/libuv/blob/v1.x/SUPPORTED_PLATFORMS.md>`_
that are likely to work with gevent. libuv `maintains a public CI
infrastructure <https://ci.nodejs.org/view/libuv/>`_.
.. rubric:: When To Use libuv
- You want to use PyPy on Windows.
- You want to develop on Windows (Windows is not recommended for
production).
- You want to use an operating system not supported by libev such as
IBM i.
.. note::
Platforms other than Linux, macOS and Windows are not
tested by gevent.
.. _libuv-limits:
Limitations and Differences
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Because of its newness, and because of some design decisions inherent
in the library and the ecosystem, there are some limitations and
differences in the way gevent behaves using libuv compared to libev.
- Timers (such as ``gevent.sleep`` and ``gevent.Timeout``) only
support a resolution of 1ms (in practice, it's closer to 1.5ms).
Attempting to use something smaller will automatically increase it
to 1ms and issue a warning. Because libuv only supports millisecond
resolution by rounding a higher-precision clock to an integer number
of milliseconds, timers apparently suffer from more jitter.
- Using negative timeouts may behave differently from libev.
- libuv blocks delivery of all signals, so signals are handled using
an (arbitrary) 0.3 second timer. This means that signal handling
will be delayed by up to that amount, and that the longest the
event loop can sleep in the operating system's ``poll`` call is
that amount. Note that this is what gevent does for libev on
Windows too.
- libuv only supports one io watcher per file descriptor, whereas
libev and gevent have always supported many watchers using
different settings. The libev behaviour is emulated at the Python
level.
- Looping multiple times and expecting events for the same file
descriptor to be raised each time without any data being read or
written (as works with libev) does not appear to work correctly on
Linux when using ``gevent.select.poll`` or a monkey-patched
``selectors.PollSelector``.
- If anything unexpected happens, libuv likes to ``abort()`` the
entire process instead of reporting an error. For example, closing
a file descriptor it is using in a watcher may cause the entire
process to be exited.
- The order in which timers and other callbacks are invoked may be
different than in libev. In particular, timers and IO callbacks
happen in a different order, and timers may easily be off by up to
half of the nominal 1ms resolution. See :issue:`1057`.
- There is no support for priorities within classes of watchers. libev
has some support for priorities and this is exposed in the low-level
gevent API, but it was never documented.
- Low-level ``fork`` and ``child`` watchers are not available. gevent
emulates these in Python on platforms that supply :func:`os.fork`.
Child watchers use ``SIGCHLD``, just as on libev, so the same
caveats apply.
- Low-level ``prepare`` watchers are not available. gevent uses
prepare watchers for internal purposes. If necessary, this could be
emulated in Python.
Performance
===========
In the various micro-benchmarks gevent has, performance among all three
loop implementations is roughly the same. There doesn't seem to be a
clear winner or loser.
.. _libev: http://software.schmorp.de/pkg/libev.html
.. _libuv: http://libuv.org
.. LocalWords: gevent libev cffi PyPy CFFI libuv FreeBSD CPython Cython