Add some event loop methods returning Futures. Lots of TBD.

This commit is contained in:
Guido van Rossum 2012-12-13 11:05:52 -08:00
parent 36562fee3e
commit 56159e8da3
1 changed files with 51 additions and 12 deletions

View File

@ -147,6 +147,7 @@ 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.
Also think of adding subheadings.
- ``run()``. Runs the event loop until there is nothing left to do.
This means, in particular:
@ -177,11 +178,53 @@ A conforming event loop object has the following methods:
- ``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
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)``.)
Some methods return Futures:
- ``wrap_future(future)``. This takes a PEP 3148 Future (i.e., an
instance of ``concurrent.futures.Future``) and returns a Future
compatible with this event loop.
- ``run_in_executor(executor, function, *args)``. Arrange to call
``function(*args)`` in an executor (see PEP 3148). Returns a Future
whose result on success is the return value that call. This is
equivalent to ``wrap_future(executor.submit(function, *args))``. If
``executor`` is ``None``, a default ``ThreadPoolExecutor`` with 5
threads is used. (TBD: Should the default executor be shared
between different event loops? Should we even have a default
executor? Should be be able to set its thread count? Shoul we even
have this method?)
- ``getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)``.
Similar to the ``socket.getaddrinfo()`` function but returns a
Future. The Future's result on success will be a list of the same
format as returned by ``socket.getaddrinfo()``. The default
implementation calls ``socket.getaddrinfo()`` using
``run_in_executor()``, but other implementations may choose to
implement their own DNS lookup.
- ``getnameinfo(sockaddr, flags)``. Similar to
``socket.getnameinfo()`` but returns a Future. The Future's result
on success will be a tuple ``(host, port)``. Same implementation
remarks as for ``getaddrinfo()``.
- ``create_transport(...)``. Creates a transport. Returns a Future.
TBD: Signature. Do we pass in a protocol or protocol class?
- ``start_serving(...)``. Enters a loop that accepts connections.
TBD: Signature. This definitely takes a protocol class. Do we pass
in a non-blocking socket that's already bound and listening, or do
we pass in arguments that let it create and configure the socket
properly? (E.g. the event loop may know a better default backlog
value for ``listen()`` than the typical application developer.)
TBD: What does it return? Something we can use to stop accepting
without stopping the event loop? A Future that completes when the
socket is successfully accepting connections?
The following methods for registering callbacks for file descriptors
are optional. If they are not implemented, accessing the method
(without calling it) returns AttributeError. The default
@ -235,16 +278,12 @@ supports it). The socket argument has to be a non-blocking socket.
socket must be in listening mode and bound to an address. Returns a
``Future`` whose result on success will be a tuple ``(conn, peer)``
where ``conn`` is a connected non-blocking socket and ``peer`` is
the peer address.
the peer address. (TBD: People tell me that this style of API is
too slow for high-volume servers. So there's also
``start_serving()`` above.)
Other TBD:
- 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.
- TBD: Do we need introspection APIs? E.g. asking for the read
callback given a file descriptor. Or when the next scheduled call
is. Or the list of file descriptors registered with callbacks.
@ -315,11 +354,11 @@ 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.
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
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::
@ -345,10 +384,10 @@ Open Issues
will be called in the right context.)
- Returning a Future is relatively expensive, and it is quite possible
that some types of calls _usually_ complete immediately
that some types of calls *usually* complete immediately
(e.g. writing small amounts of data to a socket). A trick used by
Richard Oudkerk in the tulip project's proactor branch makes calls
like recv() either return a regular result or _raise_ a Future. The
like recv() either return a regular result or *raise* a Future. The
caller (likely a transport) must then write code like this::
try: