Add some event loop methods returning Futures. Lots of TBD.
This commit is contained in:
parent
36562fee3e
commit
56159e8da3
63
pep-3156.txt
63
pep-3156.txt
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue