
Cleaning up, clarifying, trying to understand... Tim Peters wrote:
[Christian Tismer]
I tried the most simple thing, and this seemed to be duplicating the current state of the machine. The frame holds the stack, and references to all objects. By chance, the locals are not in a dict, but unpacked into the frame. (Sometimes I agree with Guido, that optimization is considered harmful :-)
I don't see that the locals are a problem here -- provided you simply leave them alone <wink>.
This depends on wether I have to duplicate frames or not. Below...
The Python stack, besides its intermingledness with the machine stack, is basically its chain of frames.
Right.
The value stack pointer still hides in the machine stack, but that's easy to change.
I'm not sure what "value stack" means here, or "machine stack". The latter means the C stack? Then I don't know which values you have in mind that are hiding in it (the locals are, as you say, unpacked in the frame, and the evaluation stack too). By "evaluation stack" I mean specifically f->f_valuestack; the current *top* of stack pointer (specifically stack_pointer) lives in the C stack -- is that what we're talking about?
Exactly!
Whichever, when we're talking about the code, let's use the names the code uses <wink>.
The evaluation stack pointer is a local variable in the C stack and must be written to the frame to become independant from the C stack. Sounds better now?
So the real Scheme-like part is this chain, methinks, with the current bytecode offset and value stack info.
Curiously, f->f_lasti is already materialized every time we make a call, in order to support tracing. So if capturing a continuation is done via a function call (hard to see any other way it could be done <wink>), a bytecode offset is already getting saved in the frame object.
You got me. I'm just completing what is partially there.
Making a copy of this in a restartable way means to increase the refcount of all objects in a frame.
You later had a vision of splitting the frame into two objects -- I think.
My wrong wording. Not splitting, but duplicting. If a frame is the current state, I make it two frames to have two current states. One will be saved, the other will be run. This is what I call "splitting". Actually, splitting must occour whenever a frame can be reached twice, in order to keep elements alive.
Whichever part the locals live in should not be copied at all, but merely have its (single) refcount increased. The other part hinges on details of your approach I don't know. The nastiest part seems to be f->f_valuestack, which conceptually needs to be (shallow) copied in the current frame and in all other frames reachable from the current frame's continuation (the chain rooted at f->f_back today); that's the sum total (along with the same frames' bytecode offsets) of capturing the control flow state.
Well, I see. You want one locals and one globals, shared by two incarnations. Gets me into trouble.
Would it be correct to undo the effect of fast locals before splitting, and redoing it on activation?
Unsure what splitting means, but in any case I can't conceive of a reason for doing anything to the locals. Their values aren't *supposed* to get restored upon continuation invocation, so there's no reason to do anything with their values upon continuation creation either. Right? Or are we talking about different things?
Let me explain. What Python does right now is: When a function is invoked, all local variables are copied into fast_locals, well of course just references are copied and counts increased. These fast locals give a lot of speed today, we must have them. You are saying I have to share locals between frames. Besides that will be a reasonable slowdown, since an extra structure must be built and accessed indirectly (right now, i's all fast, living in the one frame buffer), I cannot say that I'm convinced that this is what we need. Suppose you have a function def f(x): # do something ... # in some context, wanna have a snapshot global snapshot # initialized to None if not snapshot: snapshot = callcc.new() # continue computation x = x+1 ... What I want to achieve is that I can run this again, from my snapshot. But with shared locals, my parameter x of the snapshot would have changed to x+1, which I don't find useful. I want to fix a state of the current frame and still think it should "own" its locals. Globals are borrowed, anyway. Class instances will anyway do what you want, since the local "self" is a mutable object. How do you want to keep computations independent when locals are shared? For me it's just easier to implement and also to think with the shallow copy. Otherwise, where is my private place? Open for becoming convinced, of course :-) ciao - chris -- 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