continuations for the curious

Howdy, my modules are nearly ready. I will be out of my office for two weeks, but had no time to finalize and publish yet. Stackless Python has reached what I wanted it to reach: A continuation can be saved at every opcode. The continuationmodule has been shrunk heavily. Some extension is still needed, continuations are still frames, but they can be picked like Sam wanted it originally. Sam, I'm pretty sure this is more than enough for coroutines. Just have a look at getpcc(), this is now very easy. All involved frames are armed so that they *can* save themselves, but will do so just if necessary. The cheapest solution I could think of, no other optimization is necessary. If your coroutine functions like to swap two frames, and if they manage to do so that the refcount of the target stays at one, no extra frame will be generated. That's it, really. If someone wants to play, get the stackless module, replace ceval.c, and build continuationmodule.c as a dll or whatever. testct.py contains a lot of crap. The first implementation of class coroutine is working right. The second one is wrong by concept. later - chris ftp://ftp.pns.cc/pub/veryfar.zip -- Christian Tismer :^) <mailto:tismer@appliedbiometrics.com> Applied Biometrics GmbH : Have a break! Take a ride on Python's Kaiserin-Augusta-Allee 101 : *Starship* http://starship.python.net 10553 Berlin : PGP key -> http://wwwkeys.pgp.net PGP Fingerprint E182 71C7 1A9D 66E9 9D15 D3CC D4D7 93E2 1FAE F6DF we're tired of banana software - shipped green, ripens at home

Hey Chris, I think you're missing some include files from 'veryfar.zip'? ceval.c: In function `PyEval_EvalCode': ceval.c:355: warning: return makes pointer from integer without a cast ceval.c: In function `PyEval_EvalCode_nr': ceval.c:375: `Py_UnwindToken' undeclared (first use this function) ceval.c:375: (Each undeclared identifier is reported only once ceval.c:375: for each function it appears in.) ceval.c: In function `eval_code2_setup': ceval.c:490: structure has no member named `f_execute' ceval.c:639: structure has no member named `f_first_instr' ceval.c:640: structure has no member named `f_next_instr' -Sam

I've been playing for a bit, trying to write my own coroutine class (obeying the law of "you won't understand it until you write it yourself") based on one I've worked up for 'lunacy'. I think I have it, let me know what you think:
Differences: 1) callcc wraps the 'escape frame' with a lambda, so that it can be invoked like any other function. this actually simplifies the bootstrapping, because starting the function is identical to resuming it. 2) the coroutine object keeps track of who resumed it, so that it can resume the caller without having to know who it is. 3) the coroutine class keeps track of which is the currently 'active' coroutine. It's currently a class variable, but I think this can lead to leaks, so it might have to be made a global. +----------------------------------------------------------------- | For those folks (like me) that were confused about where to get | all the necessary files for building the latest Stackless Python, | here's the procedure: | | 1) unwrap a fresh copy of 1.5.2 | 2) unzip | http://www.pns.cc/anonftp/pub/stackless_990713.zip | on top of it | 3) then, unzip | ftp://ftp.pns.cc/pub/veryfar.zip | on top of that | 4) add "continuation continuationmodule.c" to Modules/Setup -Sam # -*- Mode: Python; tab-width: 4 -*- import continuation def callcc (fun, *args, **kw): k = continuation.getpcc(2) # eerie, that numeral two kfun = lambda v,k=k: continuation.putcc (k, v) return apply (fun, (kfun,)+args, kw) class coroutine: current = None def __init__ (self, f, *a, **kw): self.state = lambda v,f=f,a=a,kw=kw: apply (f, a, kw) self.caller = None def resume (self, value=None): caller = coroutine.current callcc (caller._save) self.caller = caller coroutine.current = self self.state (value) def _save (self, state): self.state = state def resume_caller (value): me = coroutine.current me.caller.resume (value) def resume_main (value): main.resume (value) main = coroutine (None) coroutine.current = main # counter/generator def counter (start=0, step=1): n = start while 1: resume_caller (n) n = n + step # same-fringe def _tree_walker (t): if type(t) is type([]): for x in t: _tree_walker (x) else: resume_caller (t) def tree_walker (t): _tree_walker (t) resume_caller (None) def same_fringe (t1, t2): co1 = coroutine (tree_walker, t1) co2 = coroutine (tree_walker, t2) while 1: leaf1 = co1.resume() leaf2 = co2.resume() print 'leaf1: %s leaf2: %s' % (leaf1, leaf2) if leaf1 == leaf2: if leaf1 is None: return 1 else: return 0

Sam Rushing wrote:
Thanks... this guide made me actually try it ;-)
It seems that Christian forgot the directory information in this ZIP file. You have to move the continuationmodule.c file to Modules/ by hand.
| 4) add "continuation continuationmodule.c" to Modules/Setup
-- Marc-Andre Lemburg ______________________________________________________________________ Y2000: 157 days left Business: http://www.lemburg.com/ Python Pages: http://www.lemburg.com/python/

Hey Chris, I think you're missing some include files from 'veryfar.zip'? ceval.c: In function `PyEval_EvalCode': ceval.c:355: warning: return makes pointer from integer without a cast ceval.c: In function `PyEval_EvalCode_nr': ceval.c:375: `Py_UnwindToken' undeclared (first use this function) ceval.c:375: (Each undeclared identifier is reported only once ceval.c:375: for each function it appears in.) ceval.c: In function `eval_code2_setup': ceval.c:490: structure has no member named `f_execute' ceval.c:639: structure has no member named `f_first_instr' ceval.c:640: structure has no member named `f_next_instr' -Sam

I've been playing for a bit, trying to write my own coroutine class (obeying the law of "you won't understand it until you write it yourself") based on one I've worked up for 'lunacy'. I think I have it, let me know what you think:
Differences: 1) callcc wraps the 'escape frame' with a lambda, so that it can be invoked like any other function. this actually simplifies the bootstrapping, because starting the function is identical to resuming it. 2) the coroutine object keeps track of who resumed it, so that it can resume the caller without having to know who it is. 3) the coroutine class keeps track of which is the currently 'active' coroutine. It's currently a class variable, but I think this can lead to leaks, so it might have to be made a global. +----------------------------------------------------------------- | For those folks (like me) that were confused about where to get | all the necessary files for building the latest Stackless Python, | here's the procedure: | | 1) unwrap a fresh copy of 1.5.2 | 2) unzip | http://www.pns.cc/anonftp/pub/stackless_990713.zip | on top of it | 3) then, unzip | ftp://ftp.pns.cc/pub/veryfar.zip | on top of that | 4) add "continuation continuationmodule.c" to Modules/Setup -Sam # -*- Mode: Python; tab-width: 4 -*- import continuation def callcc (fun, *args, **kw): k = continuation.getpcc(2) # eerie, that numeral two kfun = lambda v,k=k: continuation.putcc (k, v) return apply (fun, (kfun,)+args, kw) class coroutine: current = None def __init__ (self, f, *a, **kw): self.state = lambda v,f=f,a=a,kw=kw: apply (f, a, kw) self.caller = None def resume (self, value=None): caller = coroutine.current callcc (caller._save) self.caller = caller coroutine.current = self self.state (value) def _save (self, state): self.state = state def resume_caller (value): me = coroutine.current me.caller.resume (value) def resume_main (value): main.resume (value) main = coroutine (None) coroutine.current = main # counter/generator def counter (start=0, step=1): n = start while 1: resume_caller (n) n = n + step # same-fringe def _tree_walker (t): if type(t) is type([]): for x in t: _tree_walker (x) else: resume_caller (t) def tree_walker (t): _tree_walker (t) resume_caller (None) def same_fringe (t1, t2): co1 = coroutine (tree_walker, t1) co2 = coroutine (tree_walker, t2) while 1: leaf1 = co1.resume() leaf2 = co2.resume() print 'leaf1: %s leaf2: %s' % (leaf1, leaf2) if leaf1 == leaf2: if leaf1 is None: return 1 else: return 0

Sam Rushing wrote:
Thanks... this guide made me actually try it ;-)
It seems that Christian forgot the directory information in this ZIP file. You have to move the continuationmodule.c file to Modules/ by hand.
| 4) add "continuation continuationmodule.c" to Modules/Setup
-- Marc-Andre Lemburg ______________________________________________________________________ Y2000: 157 days left Business: http://www.lemburg.com/ Python Pages: http://www.lemburg.com/python/
participants (3)
-
Christian Tismer
-
M.-A. Lemburg
-
Sam Rushing