Add optional socket calls returning a Future. Some TBD rearrangements.

This commit is contained in:
Guido van Rossum 2012-12-13 10:30:42 -08:00
parent bd2ff471a3
commit 36562fee3e
1 changed files with 60 additions and 16 deletions

View File

@ -157,6 +157,13 @@ A conforming event loop object has the following methods:
- No more registered file descriptors. It is up to the registering - No more registered file descriptors. It is up to the registering
party to unregister a file descriptor when it is closed. party to unregister a file descriptor when it is closed.
- TBD: 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?
- TBD: Do we need an API to run the event loop for a little while
(e.g. a single iteration)? If so, exactly what should it do?
- ``call_later(when, callback, *args)``. Arrange for - ``call_later(when, callback, *args)``. Arrange for
``callback(*args)`` to be called approximately ``when`` seconds in ``callback(*args)`` to be called approximately ``when`` seconds in
the future, once, unless canceled. As usual in Python, ``when`` may the future, once, unless canceled. As usual in Python, ``when`` may
@ -176,9 +183,15 @@ A conforming event loop object has the following methods:
``ev.call_soon_threadsafe(ev.call_later, when, callback, *args)``.) ``ev.call_soon_threadsafe(ev.call_later, when, callback, *args)``.)
The following methods for registering callbacks for file descriptors The following methods for registering callbacks for file descriptors
are optional. The default implementation provides them but the user are optional. If they are not implemented, accessing the method
normally doesn't use these directly -- they are used by the transport (without calling it) returns AttributeError. The default
implementations exclusively: implementation provides them but the user normally doesn't use these
directly -- they are used by the transport implementations
exclusively. Also, on Windows these may be present or not depending
on whether a select-based or IOCP-based event loop is used. These
take integer file descriptors only, not objects with a fileno()
method. The file descriptor should represent something pollable --
i.e. no disk files.
- ``add_reader(fd, callback, *args)``. Arrange for - ``add_reader(fd, callback, *args)``. Arrange for
``callback(*args)`` to be called whenever file descriptor ``fd`` is ``callback(*args)`` to be called whenever file descriptor ``fd`` is
@ -201,12 +214,41 @@ implementations exclusively:
- ``remove_writer(fd)``. This is to ``add_writer()`` as - ``remove_writer(fd)``. This is to ``add_writer()`` as
``remove_reader()`` is to ``add_reader()``. ``remove_reader()`` is to ``add_reader()``.
The following methods for doing async I/O on sockets are optional.
They are alternative to the previous set of optional methods, intended
for transport implementations on Windows using IOCP (if the event loop
supports it). The socket argument has to be a non-blocking socket.
- ``sock_recv(sock, n)``. Receive up to ``n`` bytes from socket
``sock``. Returns a ``Future`` whose result on success will be a
bytes object on success.
- ``sock_sendall(sock, data)``. Send bytes ``data`` to the socket
``sock``. Returns a ``Future`` whose result on success will be
``None``. (TBD: Is it better to emulate ``sendall()`` or ``send()``
semantics?)
- ``sock_connect(sock, address)``. Connect to the given address.
Returns a ``Future`` whose result on success will be ``None``.
- ``sock_accept(sock)``. Accept a connection from a socket. The
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.
Other TBD:
- TBD: A method to submit a call to a PEP 3148 executor. Or a method - TBD: A method to submit a call to a PEP 3148 executor. Or a method
to wait for a PEP 3148 Future. Or both. to wait for a PEP 3148 Future. Or both.
- TBD: Methods that return ``Futures``, in particular to make a - TBD: Methods that return ``Futures``, in particular to make a
connection and to set up a listener. 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.
Callback Sequencing Callback Sequencing
------------------- -------------------
@ -218,7 +260,7 @@ in the order in which they are registered. For example::
guarantees that ``foo()`` is called before ``bar()``. guarantees that ``foo()`` is called before ``bar()``.
If ``call_soon()`` is used, this guarantee is even true if the system If ``call_soon()`` is used, this guarantee is true even if the system
clock were to run backwards. This is also the case for clock were to run backwards. This is also the case for
``call_later(0, callback, *args)``. However, if ``call_later()`` is ``call_later(0, callback, *args)``. However, if ``call_later()`` is
used with a nonzero ``when`` argument, all bets are off if the system used with a nonzero ``when`` argument, all bets are off if the system
@ -242,6 +284,8 @@ The DelayedCall Class
TBD. (Only one method, ``cancel()``, and a read-only property, TBD. (Only one method, ``cancel()``, and a read-only property,
``canceled``. Perhaps also ``callback`` and ``args`` properties.) ``canceled``. Perhaps also ``callback`` and ``args`` properties.)
TBD: Find a better name?
Futures Futures
------- -------
@ -292,16 +336,6 @@ policy.)
Open Issues 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 - Should we have ``future.add_callback(callback, *args)``, using the
convention from the section "Callback Style" above, or should we convention from the section "Callback Style" above, or should we
stick with the PEP 3148 specification of stick with the PEP 3148 specification of
@ -310,8 +344,18 @@ Open Issues
name since add_done_callback() does not guarantee that the callback name since add_done_callback() does not guarantee that the callback
will be called in the right context.) will be called in the right context.)
- Do we need introspection APIs? E.g. asking for the read callback - Returning a Future is relatively expensive, and it is quite possible
given a file descriptor. 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
caller (likely a transport) must then write code like this::
try:
res = ev.sock_recv(sock, 8192)
except Future as f:
yield from sch.block_future(f)
res = f.result()
Acknowledgments Acknowledgments