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
|
||||
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
|
||||
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
|
||||
``get_event_loop()`` and ``set_event_loop(eventloop)`` behaving like
|
||||
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()``.
|
||||
|
||||
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
|
||||
--------------------
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
===============
|
||||
|
|
Loading…
Reference in New Issue