updated README.twisted
This commit is contained in:
@@ -1,24 +1,55 @@
|
|||||||
|
--work in progress--
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
Twisted provides solid foundation for asynchronous programming in Python.
|
||||||
|
Eventlet makes asynchronous programming look like synchronous, thus
|
||||||
|
achieving higher signal-to-noise ratio than traditional twisted programs have.
|
||||||
|
|
||||||
|
Eventlet working on top of twisted, provides:
|
||||||
|
* [stable twisted]
|
||||||
|
* [usable and readable synchronous style]
|
||||||
|
* existing twisted code can be used without any changes
|
||||||
|
* existing blocking code can be used after trivial changes applied
|
||||||
|
|
||||||
|
|
||||||
|
Coroutines
|
||||||
|
----------
|
||||||
|
|
||||||
To understand how eventlet works, one has to understand how to use greenlet:
|
To understand how eventlet works, one has to understand how to use greenlet:
|
||||||
http://codespeak.net/py/dist/greenlet.html
|
http://codespeak.net/py/dist/greenlet.html
|
||||||
|
|
||||||
|
Essential points
|
||||||
|
|
||||||
* Every greenlet except MAIN has a parent. When an exception happen in the greenlet
|
* Every greenlet except MAIN has a parent. When an exception happen in the greenlet
|
||||||
it is propogated to the parent greenlet (special case is GreenletExit, in that case
|
it is propogated to the parent greenlet (special case is GreenletExit, in that case
|
||||||
the greenlet silently dies)
|
the greenlet silently dies)
|
||||||
* Parent can be reassigned (cycle would be detected and rejected with ValueError)
|
* Parent can be reassigned (cycle would be detected and rejected with ValueError)
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
Twisted's reactor and eventlet's hubs are very similar in what they do.
|
greenlet == coroutine == green thread == microthread in this document
|
||||||
Both do select(poll or similar) on the list of known descriptors and call the
|
|
||||||
associated callback in case of an event. In addition, both maintain scheduled calls.
|
|
||||||
|
|
||||||
However, while with twisted you explicitly start the main loop (reactor.run()),
|
|
||||||
with evenlet the main loop is run in a separate greenlet (child of the main greenlet) and
|
|
||||||
is started implicitly. Whenever you want to do a blocking operation, you switch() into
|
|
||||||
the main loop's greenlet with some additional preparations, as shown in the following figure.
|
|
||||||
|
|
||||||
blocking operation RECV on socket d:
|
How does eventlet work
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Twisted's reactor and eventlet's hub are very similar in what they do.
|
||||||
|
Both continuously call select (or poll or similar) on the list of registered
|
||||||
|
descriptors (this is called polling) and each time a specific event is
|
||||||
|
fired, the associated callback function is called. In addition, both
|
||||||
|
maintain a list of scheduled calls.
|
||||||
|
|
||||||
|
Polling is performed by the main loop - a function that both reactor and hub have.
|
||||||
|
However, while with twisted you explicitly start it by calling reactor.run(), with
|
||||||
|
eventlet the main loop is running in a dedicated greenlet and is started implicitly
|
||||||
|
upon the first switch into that greenlet. When an event is fired, the callback
|
||||||
|
perform switch to the waiting greenlet.
|
||||||
|
|
||||||
|
So, to perform a `blocking' operation that blocks this greenlet but not the others,
|
||||||
|
you switch() into the main loop's greenlet with some additional preparations,
|
||||||
|
as shown in the following figure.
|
||||||
|
|
||||||
|
= blocking operation RECV on socket d =
|
||||||
|
|
||||||
user's greenlet (USER) main loop's greenlet (MAIN_LOOP)
|
user's greenlet (USER) main loop's greenlet (MAIN_LOOP)
|
||||||
|
|
|
|
||||||
@@ -27,12 +58,36 @@ user's greenlet (USER) main loop's greenlet (MAIN_LOOP)
|
|||||||
add_descriptor(d, RECV)
|
add_descriptor(d, RECV)
|
||||||
|
|
|
|
||||||
data=MAIN_LOOP.switch() ---------> poll for events
|
data=MAIN_LOOP.switch() ---------> poll for events
|
||||||
|
|
^---------------------\ |
|
||||||
... ---------------------------> may execute other greenlets here
|
| ... ---------------------------> may execute other greenlets here
|
||||||
|
|
| |
|
||||||
event RECV on descriptor d?
|
| event RECV on descriptor d?
|
||||||
|
|
| |
|
||||||
data = d.recv() # calling blocking op that will return immediately
|
| d.remove_descriptor(d, RECV)
|
||||||
|
|
| |
|
||||||
return data <------------------- USER.switch(data)
|
| data = d.recv() # calling blocking op that will return immediately
|
||||||
|
| |
|
||||||
|
\--------- USER.switch(data) # argument data here becomes return value in user's switch
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
TBW:
|
||||||
|
|
||||||
|
* green package
|
||||||
|
modules that mimic modules in standard library, but actually do the non-blocking stuff as described above.
|
||||||
|
|
||||||
|
* how to convert protocol implemented in twisted to `blocking' mode.
|
||||||
|
see eventlet.twisteds.util.block_on
|
||||||
|
|
||||||
|
One can also look at Corotwine for an ideas.
|
||||||
|
Corotwine is another attempt to use twisted together with greentlets.
|
||||||
|
protocol <-> buffer <-> greenlet
|
||||||
|
|
||||||
|
* how to convert real blocking code.
|
||||||
|
For simple cases: block_on(reactor.callInThread(func, args))
|
||||||
|
|
||||||
|
When single deferred is not enough: Queue subclass that has list of
|
||||||
|
associated channels with it and a descriptor (obtained with os.pipe()).
|
||||||
|
Every time, queue.put is called, eQueue does descriptor.write(byte).
|
||||||
|
The descriptor is registered with the hub, so the callback is called
|
||||||
|
which gets the value from the Queue and puts it in all associated channels.
|
||||||
|
Reference in New Issue
Block a user