Clarify start_serving(). Add a section on coroutines and protocols.

This commit is contained in:
Guido van Rossum 2012-12-14 10:13:20 -08:00
parent 3e3fce8960
commit a3fa07058d
1 changed files with 53 additions and 12 deletions

View File

@ -224,14 +224,35 @@ Some methods return Futures:
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?
TBD: Signature. There are two possibilities:
1. You pass it a non-blocking socket that you have already prepared
with ``bind()`` and ``listen()`` (these system calls do not block
AFAIK), a protocol factory (I hesitate to use this word :-), and
optional flags that control the transport creation (e.g. ssl).
2. Instead of a socket, you pass it a host and port, and some more
optional flags (e.g. to control IPv4 vs IPv6, or to set the
backlog value to be passed to ``listen()``).
In either case, once it has a socket, it will wrap it in a
transport, and then enter a loop accepting connections (the best way
to implement such a loop depends on the platform). Each time a
connection is accepted, a transport and protocol are created for it.
This should return an object that can be used to control the serving
loop, e.g. to stop serving, abort all active connections, and (if
supported) adjust the backlog or other parameters. It may also have
an API to inquire about active connections. If version (2) is
selected, it should probably return a Future whose result on success
will be that control object, and which becomes done once the accept
loop is started.
TBD: It may be best to use version (2), since on some platforms the
best way to start a server may not involve sockets (but will still
involve transports and protocols).
TBD: Be more specific.
The following methods for registering callbacks for file descriptors
are optional. If they are not implemented, accessing the method
@ -515,7 +536,7 @@ Protocols
---------
Protocols are always used in conjunction with transports. While a few
common protocols are provided (e.g. decent though not necessary
common protocols are provided (e.g. decent though not necessarily
excellent HTTP client and server implementations), most protocols will
be implemented by user code or third-party libraries.
@ -588,6 +609,9 @@ it is perfectly fine to write code using callbacks only. On the other
hand, there is only one implementation of the scheduler/coroutine API,
and if you're using coroutines, that's the one you're using.
Coroutines
----------
A coroutine is a generator that follows certain conventions. For
documentation purposes, all coroutines should be decorated with
``@tulip.coroutine``, but this cannot be strictly enforced.
@ -595,8 +619,8 @@ documentation purposes, all coroutines should be decorated with
Coroutines use the ``yield from`` syntax introduced in PEP 380,
instead of the original ``yield`` syntax.
Unfortunately, the word "coroutine", like the word "generator", is
used for two different (though related) concepts:
The word "coroutine", like the word "generator", is used for two
different (though related) concepts:
- The function that defines a coroutine (a function definition
decorated with ``tulip.coroutine``). If disambiguation is needed,
@ -678,6 +702,23 @@ implemented by the ``Task`` and ``Future`` classes using only the
public interface of the event loop, so it will work with third-party
event loop implementations, too.
Coroutines and Protocols
------------------------
The best way to use coroutines to implement protocols is probably to
use a streaming buffer that gets filled by ``data_received()`` and can
be read asynchronously using methods like ``read(n)`` and
``readline()`` that return a Future. When the connection is closed,
``read()`` should return a Future whose result is ``b''``, or raise an
exception if ``connection_closed()`` is called with an exception.
To write, the ``write()`` method (and friends) on the transport can be
used -- these do not return Futures. A standard protocol
implementation should be provided that sets this up and kicks off the
coroutine when ``connection_made()`` is called.
TBD: Be more specific.
Open Issues
===========
@ -722,7 +763,7 @@ Open Issues
syntax). Anyway, I think all of these are easy enough to write
using ``Task``.
- Priorities?
- Task or callback priorities? (I hope not.)
Acknowledgments