[Stackless] micro-threads & co-routines
tismer at appliedbiometrics.com
Wed Oct 11 13:05:27 CEST 2000
Just van Rossum wrote:
> First of all: thanks Chris, for fixing The Leak and releasing an update!
You're wlecome. It was overdue. I hope it also fixes
all of the MAC leaks which I couldn't reproduce.
> A few random thoughts.
> - In the coro list (at egroups) I've proposed a co-routine API
> and have presented an implementation of it. I think it's fairly
> solid (apart from some continuation compatibility issues that
> only Chris understands..). Is it an idea to include it with
> the stackless distro? (It happens to be included in the Mac
> binary, as that's simply my working copy...)
I think this API will be the right thing (with tiny changes)
after an overhaul of Stackless.
In a former message on this list, you mentioned that
continuations are way too powerful for coroutines.
This is not true (or will loose today's truth, soon:).
After out private discussions of this stuff, I realized
that the real difficulty with current continuations is
that they always want to produce a copy when they are
touched. In many cases (like almost all the time with coroutines)
this is not necessary and a waste.
I am working on a new implementation design of Stackless
that makes this all a no-brainer, and incredibly simplifies
the implementation of all of this.
- Core idea number one is your frame split idea. For all others
who don't know about it:
At the moment, Stackless tries to keep as much binary
compatibility as possible. This makes the implementation
of continuations very hairy. There are many callbacks
to protect frames from improper treatment, and frames
are artificially turned into continuation frames.
With a lot of hair pulling, I managed to keep track
of the number of executaböe references to a frame, and
to decide when to split off a copy.
Well, but I realized that theoretically my claimed
binary compatibility isn't true since some time. If an
extension module accesses frame objects, this will
already go wrong since the fields are changed.
On the other hand, I found very few extension which touch
frames at all (the only one was AXDebug from MHammond).
That tells me that I can do major changes to the implementation
without breaking much more than I do already. :-)
- The second idea is the concept of ownership. A frame
head always has exactly one owner. Excecuting a frame
head means write access to the frame head means you
must become the owner. Either there is no owner and
we just take over, or we have to do a copy. Simple.
How is it done? Well, instead of reference counting,
we keep explicit references to owners. That means,
if "sub b" is called by "sub a", "sub b" points back
to "sub a" via its f_back, but "sub a" also points
to "sub b" via its new f_owner pointer. Only that
makes b (the callee) into the owner of a (the caller).
This is no more expensive than today (where I keep
an f_referers field), but more powerful.
I will write a detailed article on that, together with
what I'm going to change:
Frames will be split into a frame head which contains the
dynamic data, and a frame body which holds (almost) the
static data (static means for the lifetime of the function
The frame heads are then basically what we call a continuation
today. It is easy to create a copy and therefore a snapshot
of the program state. This will be built directly into the frame
The difference between Just's coroutines any my continuations
now melts down to a simple issue of mutability and ownership:
A running frame head is a mutable structure which is owned
by the executor (the ceval interpreter). A continuation in
its former sense is a copy of such a structure, an immutable
snapshot. This will be extended to allow to use read-only
references to a mutable object. These continuations come
at no cost at all, since they don't try to be a multiply
re-usable program entry point, but they are simply references
to the running frame. Basically, this is rather the same as
what Just implemented. There needs to be a little more
caution for the case that such a mutable thing is activated
twice, in other words: One tries to assign a second owner.
That exactly will trigger a copy-on-write logic. This logic
will no longer be part of the continuationmodule but will
be an integral part of the new frame implementation.
With the new structure, an extension module like Just's
will no longer have to take care about any internal issue
(like todays frame protection mess).
The current continuationmodule will become a quite trivial
> - I'm pretty sure each uthread will need its own tstate:
> tstate->dict is used in some places for "custom" thread-safety
> issues (repr() is one), but I'm not sure how this will work
> in relation to system-threads and the thread[ing] modules.
This is of course an issue. Also note that exceptions with
uthreads are quite a mess at the moment: I have to stop uthreading
and wait until the exception is handled, since that stuff isn't
microthread-safe yet. This would also need its place in an "utstate".
> - Will and Mike and I should probably make up our minds, rename
> uthread9.py to uthread.py, and donate it to Chris to include
> it in the Stackless distro. Thoughts?
I think we should name it final for now and include it
in the distro, yes.
> - Does it make sense to start thinking about rewriting uthreads
> in the same fashion as my coro module (ie. without continuations)?
> It has some consequences for ceval.c, and has big consequences
> for the uthread module. Or is it better to wait until we're done
> "redesigning" stackless?
I'm pretty shure it's better to wait with this. The frame split
stuff and ownership tracking is much better as what I have today,
and I'm going to build this in without waiting for a PEP to be
accepted, and without preparing the "big move into the core".
This can be done later. But rewriting frames and the whole
call/return logic, inside frameobject.c and ceval.c appears
to be necessary, anyway.
As a good side effect, the need for traceback objects vanishes
completely! Tracebacks are just chains of frame heads, like
everything else. Traversal in the usual order is trivial, via
the f_owner chain.
Have to think a bit more, but this is very likely near to
what I'm going to implement, soon.
cheers, and sorry about that brain-dump - chris
Christian Tismer :^) <mailto:tismer at appliedbiometrics.com>
Applied Biometrics GmbH : Have a break! Take a ride on Python's
Kaunstr. 26 : *Starship* http://starship.python.net
14163 Berlin : PGP key -> http://wwwkeys.pgp.net
PGP Fingerprint E182 71C7 1A9D 66E9 9D15 D3CC D4D7 93E2 1FAE F6DF
where do you want to jump today? http://www.stackless.com
Stackless mailing list
Stackless at starship.python.net
More information about the Stackless