More about event loops.
This commit is contained in:
parent
8e31cc29eb
commit
53018c7e85
175
pep-3156.txt
175
pep-3156.txt
|
@ -111,8 +111,8 @@ Until the boring name is chosen, this PEP will use 'tulip' as the
|
||||||
toplevel package name. Classes and functions given without a module
|
toplevel package name. Classes and functions given without a module
|
||||||
name are assumed to be accessed via the toplevel package.
|
name are assumed to be accessed via the toplevel package.
|
||||||
|
|
||||||
Getting and Setting the Event Loop
|
Event Loop Policy: Getting and Setting the Event Loop
|
||||||
----------------------------------
|
-----------------------------------------------------
|
||||||
|
|
||||||
To get the current event loop, use ``get_event_loop()``. This returns
|
To get the current event loop, use ``get_event_loop()``. This returns
|
||||||
an instance of the ``EventLoop`` class defined below or an equivalent
|
an instance of the ``EventLoop`` class defined below or an equivalent
|
||||||
|
@ -131,14 +131,183 @@ To change the way ``get_event_loop()`` and ``set_event_loop()`` work
|
||||||
policy object. The policy object can be any object that has methods
|
policy object. The policy object can be any object that has methods
|
||||||
``get_event_loop()`` and ``set_event_loop(eventloop)`` behaving like
|
``get_event_loop()`` and ``set_event_loop(eventloop)`` behaving like
|
||||||
the functions described above. The default event loop policy is an
|
the functions described above. The default event loop policy is an
|
||||||
instance of the class ``EventLoopPolicy``. The current event loop
|
instance of the class ``DefaultEventLoopPolicy``. The current event loop
|
||||||
policy object can be retrieved by calling ``get_event_loop_policy()``.
|
policy object can be retrieved by calling ``get_event_loop_policy()``.
|
||||||
|
|
||||||
|
An event loop policy may but does not have to enforce that there is
|
||||||
|
only one event loop in existence. The default event loop policy does
|
||||||
|
not enforce this, but it does enforce that there is only one event
|
||||||
|
loop per thread.
|
||||||
|
|
||||||
Event Loop Interface
|
Event Loop Interface
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
A conforming event loop object has the following methods:
|
||||||
|
|
||||||
|
..
|
||||||
|
Look for a better way to format method docs. PEP 12 doesn't
|
||||||
|
seem to have one. PEP 418 uses ^^^, which makes sub-headings.
|
||||||
|
|
||||||
|
- ``run()``. Runs the event loop until there is nothing left to do.
|
||||||
|
This means, in particular:
|
||||||
|
|
||||||
|
- No more calls scheduled with ``call_later()`` (except for canceled
|
||||||
|
calls).
|
||||||
|
|
||||||
|
- No more registered file descriptors. It is up to the registering
|
||||||
|
party to unregister a file descriptor when it is closed.
|
||||||
|
|
||||||
|
- ``call_later(when, callback, *args)``. Arrange for
|
||||||
|
``callback(*args)`` to be called approximately ``when`` seconds in
|
||||||
|
the future, once, unless canceled. As usual in Python, ``when`` may
|
||||||
|
be a floating point number to represent smaller intervals. Returns
|
||||||
|
a ``DelayedCall`` object representing the callback, whose
|
||||||
|
``cancel()`` method can be used to cancel the callback.
|
||||||
|
|
||||||
|
- ``call_soon(callback, *args)``. Equivalent to ``call_later(0,
|
||||||
|
callback, *args)``.
|
||||||
|
|
||||||
|
- ``call_soon_threadsafe(callback, *args)``. Like
|
||||||
|
``call_soon(callback, *args)``, but when called from another thread
|
||||||
|
while the event loop is blocked waiting for I/O, unblocks the event
|
||||||
|
loop. This is the _only_ method that is safe to call from another
|
||||||
|
thread or from a signal handler. (To schedule a callback for a
|
||||||
|
later time in a threadsafe manner, you can use
|
||||||
|
``ev.call_soon_threadsafe(ev.call_later, when, callback, *args)``.)
|
||||||
|
|
||||||
|
- ``add_reader(fd, callback, *args)``. Arrange for
|
||||||
|
``callback(*args)`` to be called whenever file descriptor ``fd`` is
|
||||||
|
ready for reading. Returns a ``DelayedCall`` object which can be
|
||||||
|
used to cancel the callback. Note that, unlike ``call_later()``,
|
||||||
|
the callback may be called many times. Calling ``add_reader()``
|
||||||
|
again for the same file descriptor implicitly cancels the previous
|
||||||
|
callback for that file descriptor.
|
||||||
|
|
||||||
|
- ``add_writer(fd, callback, *args)``. Like ``add_reader()``,
|
||||||
|
but registers the callback for writing instead of for reading.
|
||||||
|
|
||||||
|
- ``remove_reader(fd)``. Cancels the current read callback for file
|
||||||
|
descriptor ``fd``, if one is set. A no-op if no callback is
|
||||||
|
currently set for the file descriptor. (The reason for providing
|
||||||
|
this alternate interface is that it is often more convenient to
|
||||||
|
remember the file descriptor than to remember the ``DelayedCall``
|
||||||
|
object.)
|
||||||
|
|
||||||
|
- ``remove_writer(fd)``. This is to ``add_writer()`` as
|
||||||
|
``remove_reader()`` is to ``add_reader()``.
|
||||||
|
|
||||||
|
- TBD: A method to submit a call to a PEP 3148 executor. Or a method
|
||||||
|
to wait for a PEP 3148 Future. Or both.
|
||||||
|
|
||||||
|
- TBD: Methods that return ``Futures``, in particular to make a
|
||||||
|
connection and to set up a listener.
|
||||||
|
|
||||||
|
Callback Sequencing
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
When two callbacks are scheduled for the same time, they are run
|
||||||
|
in the order in which they are registered. For example::
|
||||||
|
|
||||||
|
ev.call_soon(foo)
|
||||||
|
ev.call_soon(bar)
|
||||||
|
|
||||||
|
guarantees that ``foo()`` is called before ``bar()``.
|
||||||
|
|
||||||
|
If ``call_soon()`` is used, this guarantee is even true if the system
|
||||||
|
clock were to run backwards. This is also the case for
|
||||||
|
``call_later(0, callback, *args)``. However, if ``call_later()`` is
|
||||||
|
used with a nonzero ``when`` argument, all bets are off if the system
|
||||||
|
clock were to runs backwards. (A good event loop implementation
|
||||||
|
should use ``time.monotonic()`` to avoid problems when the clock runs
|
||||||
|
backward. See PEP 418.)
|
||||||
|
|
||||||
|
Context
|
||||||
|
-------
|
||||||
|
|
||||||
|
All event loops have a notion of context. For the default event loop
|
||||||
|
implementation, the context is a thread. An event loop implementation
|
||||||
|
should run all callbacks in the same context. An event loop
|
||||||
|
implementation should run only one callback at a time, so callbacks
|
||||||
|
can assume automatic mutual exclusion with other callbacks scheduled
|
||||||
|
in the same event loop.
|
||||||
|
|
||||||
|
The DelayedCall Class
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
TBD. (Only one method, ``cancel()``, and a read-only property,
|
||||||
|
``canceled``. Perhaps also ``callback`` and ``args`` properties.)
|
||||||
|
|
||||||
|
Futures
|
||||||
|
-------
|
||||||
|
|
||||||
TBD.
|
TBD.
|
||||||
|
|
||||||
|
Transports
|
||||||
|
----------
|
||||||
|
|
||||||
|
TBD.
|
||||||
|
|
||||||
|
Protocols
|
||||||
|
---------
|
||||||
|
|
||||||
|
TBD.
|
||||||
|
|
||||||
|
Coroutines and the Scheduler
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
TBD.
|
||||||
|
|
||||||
|
Callback Style
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Most interfaces taking a callback also take positional arguments. For
|
||||||
|
instance, to arrange for ``foo("abc", 42)`` to be called soon, you
|
||||||
|
call ``ev.call_soon(foo, "abc", 42)``. To schedule the call
|
||||||
|
``foo()``, use ``ev.call_soon(foo)``. This convention greatly reduces
|
||||||
|
the number of small lambdas required in typical callback programming.
|
||||||
|
|
||||||
|
This convention specifically does _not_ support keyword arguments.
|
||||||
|
Keyword arguments are used to pass optional extra information about
|
||||||
|
the callback. This allows graceful evolution of the API without
|
||||||
|
having to worry about whether a keyword might be significant to a
|
||||||
|
callee somewhere. If you have a callback that _must_ be called with a
|
||||||
|
keyword argument, you can use a lambda or ``functools.partial``. For
|
||||||
|
example::
|
||||||
|
|
||||||
|
ev.call_soon(functools.partial(foo, "abc", repeat=42))
|
||||||
|
|
||||||
|
Choosing an Event Loop Implementation
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
TBD. (This is about the choice to use e.g. select vs. poll vs. epoll,
|
||||||
|
and how to override the choice. Probably belongs in the event loop
|
||||||
|
policy.)
|
||||||
|
|
||||||
|
|
||||||
|
Open Issues
|
||||||
|
===========
|
||||||
|
|
||||||
|
- What have I missed that hasn't been marked with TBD yet?
|
||||||
|
|
||||||
|
- A better name for ``DelayedCall`` (I really don't like adjectives. :-)
|
||||||
|
|
||||||
|
- Do we need an API for stopping the event loop, given that we have
|
||||||
|
the termination condition? Is the termination condition compatible
|
||||||
|
with other frameworks?
|
||||||
|
|
||||||
|
- Do we need an API to run the event loop for a little while?
|
||||||
|
|
||||||
|
- Should we have ``future.add_callback(callback, *args)``, using the
|
||||||
|
convention from the section "Callback Style" above, or should we
|
||||||
|
stick with the PEP 3148 specification of
|
||||||
|
``future.add_done_callback(callback)`` which calls
|
||||||
|
``callback(future)``? (Glyph suggested using a different method
|
||||||
|
name since add_done_callback() does not guarantee that the callback
|
||||||
|
will be called in the right context.)
|
||||||
|
|
||||||
|
- Do we need introspection APIs? E.g. asking for the read callback
|
||||||
|
given a file descriptor.
|
||||||
|
|
||||||
|
|
||||||
Acknowledgments
|
Acknowledgments
|
||||||
===============
|
===============
|
||||||
|
|
Loading…
Reference in New Issue