[Python-Dev] Re: [python-coro] coroutines and microthreads

Just van Rossum just@letterror.com
Fri, 17 Nov 2000 09:55:48 +0100


At 7:42 PM -0500 16-11-2000, Guido van Rossum wrote:
[ snipp ]
>Using my proposed API this example could be rewritten a bit
>cleaner, as follows:
>
>    from coro import coroutine, suspend, EarlyExit  # Provisional module name
>
>    def fringe(L):
>	for x in L:
>	    if type(x) is type(L):
>		fringe(x)
>	    else:
>		suspend(x)
>
>    def printinorder(L):
>	c = coroutine(f, L)
>	try:
>	    while 1:
>		print c(),
>	except EarlyExit:
>	    pass
>	print
>
>    x = [0, 1, [2, [3]], [4,5], [[[6]]]]
>    printinorder(x) # prints "0 1 2 3 4 5 6"
>
>This is about as simple as it gets.  It supports both generators (like
>the example here) and coroutines (like Tim's Demo/threads/squasher.py
>example).

Neat! (It's _very_ close to what I proposed myself, so how can I not like
it? ;-)

One question: I assume values can also be passed when explicitly resuming a
coroutine. However, the _first_ time a coroutine is "resumed" (started,
really), the stored init arguments get passed to the function. What to do
with a value passed to the resume call? Two possible solutions:
1) don't not allow passing a value the first time
2) add the value(s) to the init arguments

As code:

  def fribble(initarg):
     ...

  c = coroutine(fribble, initarg)
  c(x)  # <-- where does x go? or is it not allowed?

>Besides the APIs shown here (coroutine(), suspend(), and EarlyExit) I
>propose a function current() which returns the current coroutine
>object.

Starts looking more and more like what I proposed myself...

>There should also be a way to kill a coroutine (or at least
>to send an exception).  When a coroutine falls through at its end,
>*some* other coroutine needs to be resumed.

Here's what I did in my own strawman:
- resume the guy that resumed me, if any (making it equivalent to suspend())
- if we were resumed by a suspend() call (which in my proposal means we
don't have a "resumer", to avoid endless suspend()/suspend() bouncing),
resume the "main" coroutine, which is the (non-coro) code that started the
first coroutine.

Maybe coroutines should have a kill() method, which would post the
EarlyExit exception. Maybe even a postException() method (like uthreads
have), allowing you to post arbitrary exceptions to the coroutine. Dunno.

>I believe this can be implemented with a much simplified stackless
>approach, that doesn't cater towards continuations (but still borrows
>a lot of wisdom from Christian's Stackless).  It can also be
>implemented using threads, which should give some hope for getting the
>same API supported in JPython, making it more attractive.  I am hoping
>to create an implementation on top of Stackless, just to experiment
>with the idiom.

Here's my own code, based on stackless 1.1:
   http://www.petr.com/just/corojust.tar.gz
or
   http://www.petr.com/just/corojust/
for the individual files.

Apart from the initialisation it's pretty much what you propose.

Just