Clarify start_serving(). Add a section on coroutines and protocols.
This commit is contained in:
parent
3e3fce8960
commit
a3fa07058d
65
pep-3156.txt
65
pep-3156.txt
|
@ -224,14 +224,35 @@ Some methods return Futures:
|
||||||
TBD: Signature. Do we pass in a protocol or protocol class?
|
TBD: Signature. Do we pass in a protocol or protocol class?
|
||||||
|
|
||||||
- ``start_serving(...)``. Enters a loop that accepts connections.
|
- ``start_serving(...)``. Enters a loop that accepts connections.
|
||||||
TBD: Signature. This definitely takes a protocol class. Do we pass
|
TBD: Signature. There are two possibilities:
|
||||||
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
|
1. You pass it a non-blocking socket that you have already prepared
|
||||||
properly? (E.g. the event loop may know a better default backlog
|
with ``bind()`` and ``listen()`` (these system calls do not block
|
||||||
value for ``listen()`` than the typical application developer.)
|
AFAIK), a protocol factory (I hesitate to use this word :-), and
|
||||||
TBD: What does it return? Something we can use to stop accepting
|
optional flags that control the transport creation (e.g. ssl).
|
||||||
without stopping the event loop? A Future that completes when the
|
|
||||||
socket is successfully accepting connections?
|
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
|
The following methods for registering callbacks for file descriptors
|
||||||
are optional. If they are not implemented, accessing the method
|
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
|
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
|
excellent HTTP client and server implementations), most protocols will
|
||||||
be implemented by user code or third-party libraries.
|
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,
|
hand, there is only one implementation of the scheduler/coroutine API,
|
||||||
and if you're using coroutines, that's the one you're using.
|
and if you're using coroutines, that's the one you're using.
|
||||||
|
|
||||||
|
Coroutines
|
||||||
|
----------
|
||||||
|
|
||||||
A coroutine is a generator that follows certain conventions. For
|
A coroutine is a generator that follows certain conventions. For
|
||||||
documentation purposes, all coroutines should be decorated with
|
documentation purposes, all coroutines should be decorated with
|
||||||
``@tulip.coroutine``, but this cannot be strictly enforced.
|
``@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,
|
Coroutines use the ``yield from`` syntax introduced in PEP 380,
|
||||||
instead of the original ``yield`` syntax.
|
instead of the original ``yield`` syntax.
|
||||||
|
|
||||||
Unfortunately, the word "coroutine", like the word "generator", is
|
The word "coroutine", like the word "generator", is used for two
|
||||||
used for two different (though related) concepts:
|
different (though related) concepts:
|
||||||
|
|
||||||
- The function that defines a coroutine (a function definition
|
- The function that defines a coroutine (a function definition
|
||||||
decorated with ``tulip.coroutine``). If disambiguation is needed,
|
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
|
public interface of the event loop, so it will work with third-party
|
||||||
event loop implementations, too.
|
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
|
Open Issues
|
||||||
===========
|
===========
|
||||||
|
@ -722,7 +763,7 @@ Open Issues
|
||||||
syntax). Anyway, I think all of these are easy enough to write
|
syntax). Anyway, I think all of these are easy enough to write
|
||||||
using ``Task``.
|
using ``Task``.
|
||||||
|
|
||||||
- Priorities?
|
- Task or callback priorities? (I hope not.)
|
||||||
|
|
||||||
|
|
||||||
Acknowledgments
|
Acknowledgments
|
||||||
|
|
Loading…
Reference in New Issue