[Python-Dev] Re: Coroutines

Tim Peters tim_one at email.msn.com
Sat May 22 06:22:55 CEST 1999


Thoughts o' the day:

+ Generators ("semi-coroutines") are wonderful tools and easy to implement
without major changes to the PVM.  Icon calls 'em generators, Sather calls
'em iterators, and they're exactly what you need to implement "for thing in
object:" when object represents a collection that's tricky to materialize.
Python needs something like that.  OTOH, generators are pretty much limited
to that.

+ Coroutines are more general but much harder to implement, because each
coroutine needs its own stack (a generator only has one stack *frame*-- its
own --to worry about), and C-calling-Python can get into the act.  As Sam
said, they're probably no easier to implement than call/cc (but trivial to
implement given call/cc).

+ What may be most *natural* is to forget all that and think about a
variation of Python threads implemented directly via the interpreter,
without using OS threads.  The PVM already knows how to handle thread-state
swapping.  Given Christian's stackless interpreter, and barring C->Python
cases, I suspect Python can fake threads all by itself, in the sense of
interleaving their executions within a single "real" (OS) thread.  Given the
global interpreter lock, Python effectively does only-one-at-a-time anyway.

Threads are harder than generators or coroutines to learn, but

A) Many more people know how to use them already.

B) Generators and coroutines can be implemented using (real or fake)
threads.

C) Python has offered threads since the beginning.

D) Threads offer a powerful mode of control transfer coroutines don't,
namely "*anyone* else who can make progress now, feel encouraged to do so at
my expense".

E) For whatever reasons, in my experience people find threads much easier to
learn than call/cc -- perhaps because threads are *obviously* useful upon
first sight, while it takes a real Zen Experience before call/cc begins to
make sense.

F) Simulated threads could presumably produce much more informative error
msgs (about deadlocks and such) than OS threads, so even people using real
threads could find excellent debugging use for them.

Sam doesn't want to use "real threads" because they're pigs; fake threads
don't have to be.  Perhaps

x = y.SOME_ASYNC_CALL(r, s, t)

could map to e.g.

import config
if config.USE_REAL_THREADS:
    import threading
else:
    from simulated_threading import threading

from config.shared import msg_queue

class Y:
    def __init__(self, ...):
        self.ready = threading.Event()
        ...

    def SOME_ASYNC_CALL(self, r, s, t):
        result = [None]  # mutable container to hold the result
        msg_queue.put((server_of_the_day, r, s, t, self.ready, result))
        self.ready.wait()
        self.ready.clear()
        return result[0]

where some other simulated thread polls the msg_queue and does ready.set()
when it's done processing the msg enqueued by SOME_ASYNC_CALL.  For this to
scale nicely, it's probably necessary for the PVM to cooperate with the
simulated_threading implementation (e.g., a simulated thread that blocks
(like on self.ready.wait()) should be taken out of the collection of
simulated threads the PVM may attempt to resume -- else in Sam's case the
PVM would repeatedly attempt to wake up thousands of blocked threads, and
things would slow to a crawl).

Of course, simulated_threading could be built on top of call/cc or
coroutines too.  The point to making threads the core concept is keeping
Guido's brain from exploding.  Plus, as above, you can switch to "real
threads" by changing an import statement.

making-sure-the-global-lock-support-hair-stays-around-even-if-greg-
    renders-it-moot-for-real-threads<wink>-ly y'rs  - tim






More information about the Python-Dev mailing list