From 36562fee3ee67b21b9a459fee68d0b66dc042860 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 13 Dec 2012 10:30:42 -0800 Subject: [PATCH] Add optional socket calls returning a Future. Some TBD rearrangements. --- pep-3156.txt | 76 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/pep-3156.txt b/pep-3156.txt index 15e55d2f0..c25cc0045 100644 --- a/pep-3156.txt +++ b/pep-3156.txt @@ -157,6 +157,13 @@ A conforming event loop object has the following methods: - No more registered file descriptors. It is up to the registering 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 ``callback(*args)`` to be called approximately ``when`` seconds in 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)``.) The following methods for registering callbacks for file descriptors -are optional. The default implementation provides them but the user -normally doesn't use these directly -- they are used by the transport -implementations exclusively: +are optional. If they are not implemented, accessing the method +(without calling it) returns AttributeError. The default +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 ``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_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 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. + Callback Sequencing ------------------- @@ -218,7 +260,7 @@ in the order in which they are registered. For example:: 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 ``call_later(0, callback, *args)``. However, if ``call_later()`` is 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, ``canceled``. Perhaps also ``callback`` and ``args`` properties.) +TBD: Find a better name? + Futures ------- @@ -292,16 +336,6 @@ 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 @@ -310,8 +344,18 @@ Open Issues 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. +- Returning a Future is relatively expensive, and it is quite possible + 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