Microthreads without Stackless?

Bryan Olson fakeaddress at nowhere.org
Sun Sep 19 21:16:49 EDT 2004


David Mertz, Ph.D. wrote:
 > Well, a couple things.  The article on (semi-)coroutines is really a
 > different topic than the one on "weightless threads."  They both take
 > advantage of Python generators.  And they both involve a scheduler.
 > But there are differences between the techniques... and still greater
 > differences between the motivations that would prompt the use of each.
 >
 > Unfortunately, Bryan started the discussion with a claim like
 > "weightless threads aren't *really* coroutines"... to which the answer
 > is "Duh!"

That's not a quote of me.  What I did say is, "Mertz's pattern
provides nothing like real co-routines or threads".  By "real"
coroutines I've meant coroutines without the limitations stated
for semi-coroutines.

 > Weightless threads basically amount to "cooperative multitasking."
 > Unlike in actual Stackless (or with "weighty" OS threads), the
 > weightless thread schedulers/technique depends every "thread"
 > (procedure) acting nice, and giving control back to the scheduler
 > without too much delay.

Again, I have to disagree with the first sentence there.  I've
never heard "Cooperative multitasking" to refer to calls that
were limited to a one-level depth.

I find the usual description of the difference between
cooperative and preemptive multi-threading to be deceptive. In
either system, the vast majority of thread switches are induced
by the running thread beginning an operation that blocks.



 > Some older OS's were written this way, FWIW,
 > but those had obvious problems.

Microsoft Windows and Apple Mac-OS had cooperative multi-tasking
when they took over most of the world.  They certainly had
problems, but that intra-process threading was cooperative was
not high on the problem list.  (The way separate processes could
effect each other, that was a problem.)


[...]
 > At the Python Cookbook site, Bernhard Mulder unfortunately posted a
 > recipe that he called "coroutines", when what it really does is
 > implement weightless threads.  The recipe itself is fine, but I hate
 > to see people mislead on the nomenclature:
 >
 >   http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/300019
 >

The point I got from Bernhard Mulder's recipe is how to yield
from any call depth.  His method is to re-write every call chain
that leads to a yield, so that every function is a generator and
every call is in a for loop.  With patterns of the form 'rewrite
everything like this...', any Turing-complete language can
emulate any computational facility of any other language.
That's the kind of solution I was discounting when I wrote,
"Importantly, I do *not* have to re-write all the functions in
every call chain that leads to a read or write".


 > Now it's true that Python syntax -in itself- only implements
 > semi-coroutines.  That is, you can branch INTO any procedure you want,
 > but the branch OUT OF that procedure is still stack like... i.e. to
 > the caller only.  What my article points out (which a lot of people
 > did not realize when Python generators were new) is that
 > semi-coroutines are actually fully general.

Syntax isn't the issue.  Python was just as general without
generators, which we can emulate with closures or classes.

[...]
 > The only change is that you always "yield to" a given procedures,
 > rather than either call it, or call its .next() method.  Of course,
 > lots of other flows are possible other than stack-like.  But you are
 > not prohibited from stack-like flow (example typed without testing,
 > forgive any minor typo):
 >
 >   from __future__ import generators
 >   cargo = None
 >   def grandma():
 >       yield (MOM, cargo)
 >   def mom():
 >       yield (DAUGHTER, cargo)
 >       yield (GRANDMA, cargo)
 >   def daughter():
 >       yield (MOM, cargo)
 >   GRANDMA, MOM, DAUGHTER = grandma(), mom(), daughter()
 >   scheduler(GRANDMA)

Again, that's a 'rewrite every call-chain' solution.  If we look
at the socket servers in the Python library, they're written on
a base where one server handles one client.  Then there's a
forking mix-in, and a threading mix-in, that each allow the same
code to handle many clients.  We can build higher-level
protocols on them, with the same simple form: handle one client,
and add forking/threading to handle many clients, without re-
writing the code.  Full coroutines or micro-threads can offer
the same facility.  Semi-coroutines or "weightless threads" are
not powerful enough to do that.


--
--Bryan



More information about the Python-list mailing list