Staging
v0.5.1
https://github.com/python/cpython
Raw File
Tip revision: db455296be5f792b8c12b7cd7f3962b52e4f44ee authored by Ɓukasz Langa on 23 September 2020, 12:36:32 UTC
Python 3.8.6
Tip revision: db45529
asyncio-policy.rst
.. currentmodule:: asyncio


.. _asyncio-policies:

========
Policies
========

An event loop policy is a global per-process object that controls
the management of the event loop. Each event loop has a default
policy, which can be changed and customized using the policy API.

A policy defines the notion of *context* and manages a
separate event loop per context. The default policy
defines *context* to be the current thread.

By using a custom event loop policy, the behavior of
:func:`get_event_loop`, :func:`set_event_loop`, and
:func:`new_event_loop` functions can be customized.

Policy objects should implement the APIs defined
in the :class:`AbstractEventLoopPolicy` abstract base class.


Getting and Setting the Policy
==============================

The following functions can be used to get and set the policy
for the current process:

.. function:: get_event_loop_policy()

   Return the current process-wide policy.

.. function:: set_event_loop_policy(policy)

   Set the current process-wide policy to *policy*.

   If *policy* is set to ``None``, the default policy is restored.


Policy Objects
==============

The abstract event loop policy base class is defined as follows:

.. class:: AbstractEventLoopPolicy

   An abstract base class for asyncio policies.

   .. method:: get_event_loop()

      Get the event loop for the current context.

      Return an event loop object implementing the
      :class:`AbstractEventLoop` interface.

      This method should never return ``None``.

      .. versionchanged:: 3.6

   .. method:: set_event_loop(loop)

      Set the event loop for the current context to *loop*.

   .. method:: new_event_loop()

      Create and return a new event loop object.

      This method should never return ``None``.

   .. method:: get_child_watcher()

      Get a child process watcher object.

      Return a watcher object implementing the
      :class:`AbstractChildWatcher` interface.

      This function is Unix specific.

   .. method:: set_child_watcher(watcher)

      Set the current child process watcher to *watcher*.

      This function is Unix specific.


asyncio ships with the following built-in policies:


.. class:: DefaultEventLoopPolicy

   The default asyncio policy.  Uses :class:`SelectorEventLoop`
   on Unix and :class:`ProactorEventLoop` on Windows.

   There is no need to install the default policy manually. asyncio
   is configured to use the default policy automatically.

   .. versionchanged:: 3.8

      On Windows, :class:`ProactorEventLoop` is now used by default.


.. class:: WindowsSelectorEventLoopPolicy

   An alternative event loop policy that uses the
   :class:`SelectorEventLoop` event loop implementation.

   .. availability:: Windows.


.. class:: WindowsProactorEventLoopPolicy

   An alternative event loop policy that uses the
   :class:`ProactorEventLoop` event loop implementation.

   .. availability:: Windows.

.. _asyncio-watchers:

Process Watchers
================

A process watcher allows customization of how an event loop monitors
child processes on Unix. Specifically, the event loop needs to know
when a child process has exited.

In asyncio, child processes are created with
:func:`create_subprocess_exec` and :meth:`loop.subprocess_exec`
functions.

asyncio defines the :class:`AbstractChildWatcher` abstract base class, which child
watchers should implement, and has four different implementations:
:class:`ThreadedChildWatcher` (configured to be used by default),
:class:`MultiLoopChildWatcher`, :class:`SafeChildWatcher`, and
:class:`FastChildWatcher`.

See also the :ref:`Subprocess and Threads <asyncio-subprocess-threads>`
section.

The following two functions can be used to customize the child process watcher
implementation used by the asyncio event loop:

.. function:: get_child_watcher()

   Return the current child watcher for the current policy.

.. function:: set_child_watcher(watcher)

   Set the current child watcher to *watcher* for the current
   policy.  *watcher* must implement methods defined in the
   :class:`AbstractChildWatcher` base class.

.. note::
   Third-party event loops implementations might not support
   custom child watchers.  For such event loops, using
   :func:`set_child_watcher` might be prohibited or have no effect.

.. class:: AbstractChildWatcher

   .. method:: add_child_handler(pid, callback, \*args)

      Register a new child handler.

      Arrange for ``callback(pid, returncode, *args)`` to be called
      when a process with PID equal to *pid* terminates.  Specifying
      another callback for the same process replaces the previous
      handler.

      The *callback* callable must be thread-safe.

   .. method:: remove_child_handler(pid)

      Removes the handler for process with PID equal to *pid*.

      The function returns ``True`` if the handler was successfully
      removed, ``False`` if there was nothing to remove.

   .. method:: attach_loop(loop)

      Attach the watcher to an event loop.

      If the watcher was previously attached to an event loop, then
      it is first detached before attaching to the new loop.

      Note: loop may be ``None``.

   .. method:: is_active()

      Return ``True`` if the watcher is ready to use.

      Spawning a subprocess with *inactive* current child watcher raises
      :exc:`RuntimeError`.

      .. versionadded:: 3.8

   .. method:: close()

      Close the watcher.

      This method has to be called to ensure that underlying
      resources are cleaned-up.

.. class:: ThreadedChildWatcher

   This implementation starts a new waiting thread for every subprocess spawn.

   It works reliably even when the asyncio event loop is run in a non-main OS thread.

   There is no noticeable overhead when handling a big number of children (*O(1)* each
   time a child terminates), but stating a thread per process requires extra memory.

   This watcher is used by default.

   .. versionadded:: 3.8

.. class:: MultiLoopChildWatcher

   This implementation registers a :py:data:`SIGCHLD` signal handler on
   instantiation. That can break third-party code that installs a custom handler for
   `SIGCHLD`.  signal).

   The watcher avoids disrupting other code spawning processes
   by polling every process explicitly on a :py:data:`SIGCHLD` signal.

   There is no limitation for running subprocesses from different threads once the
   watcher is installed.

   The solution is safe but it has a significant overhead when
   handling a big number of processes (*O(n)* each time a
   :py:data:`SIGCHLD` is received).

   .. versionadded:: 3.8

.. class:: SafeChildWatcher

   This implementation uses active event loop from the main thread to handle
   :py:data:`SIGCHLD` signal. If the main thread has no running event loop another
   thread cannot spawn a subprocess (:exc:`RuntimeError` is raised).

   The watcher avoids disrupting other code spawning processes
   by polling every process explicitly on a :py:data:`SIGCHLD` signal.

   This solution is as safe as :class:`MultiLoopChildWatcher` and has the same *O(N)*
   complexity but requires a running event loop in the main thread to work.

.. class:: FastChildWatcher

   This implementation reaps every terminated processes by calling
   ``os.waitpid(-1)`` directly, possibly breaking other code spawning
   processes and waiting for their termination.

   There is no noticeable overhead when handling a big number of
   children (*O(1)* each time a child terminates).

   This solution requires a running event loop in the main thread to work, as
   :class:`SafeChildWatcher`.


Custom Policies
===============

To implement a new event loop policy, it is recommended to subclass
:class:`DefaultEventLoopPolicy` and override the methods for which
custom behavior is wanted, e.g.::

    class MyEventLoopPolicy(asyncio.DefaultEventLoopPolicy):

        def get_event_loop(self):
            """Get the event loop.

            This may be None or an instance of EventLoop.
            """
            loop = super().get_event_loop()
            # Do something with loop ...
            return loop

    asyncio.set_event_loop_policy(MyEventLoopPolicy())
back to top