[Seaside] Seaside Internals
Stephen Pair
stephen at pairhome.net
Mon Sep 8 17:33:48 CEST 2003
Avi Bryant wrote:
>On Mon, 8 Sep 2003, Stephen Pair wrote:
>
>
>
>>Those links seem to only argue in favor of having continuations evaluate
>>an unwind behavior. Where are the arguments against? I can't think of
>>any reason why a normal use of continuations shouldn't invoke the unwind
>>behavior of the calling (and terminating) stack. In my opinion, any
>>flow control contructs (non local returns, exception handling,
>>continuations, etc) should honor requests to #ensure: (and friends).
>>
>>
>
>Stephen,
>
>The argument isn't really whether or not continuation invocations should
>evaluate #ensure: blocks, but when or which ones.
>
>The problem is that part of the contract of an ensure block is that it's
>the *last* thing to get evaluated - when you have
>
>[A] ensure: [B],
>
>you expect B to only get evaluated once A is done - and it may well be
>very broken to evaluate anything in A after B has done its thing.
>
>But if I capture a continuation inside A, then exit from A (evaluating
>B), I now have the power to do exactly that - jump back into the middle
>of A. And when you then leave again, does B get evaluated a second time,
>a third?
>
I see. I was speaking to the common case where you are resuming a
continuation and the active stack is being abandoned (without a
continuation to resume it)...in that case, my expectation is that unwind
behavior would be invoked. But, I see your point about resuming
continuations that happen to be inside contexts that have unwind
behavior. A solution may be to always resume a continuation in its own
process (allowing the caller to continue processing normally while the
continuation runs in another process) and to provide for a mechanism
that can restrict certain continuations to just a single invocation. A
continuation that has unwind behavior in it's stack would be restricted
to a single invocation. For example, assuming that a continuation is
resumed on a separate process, the following would be illegal for a
continuation that has unwind behavior:
aContinuation resume; resume
The first would be allowed, the second disallowed (with a cannot resume
exception or something). If you were to extend this to accommodate
exceptions, then you would also likely want continuations that cannot be
resumed at all. And, there may even be situations where you'd want to
restrict continuations to some arbitrary number of invocations.
Looking at most uses of #ensure: or #ifCurtailed:, allowing multiple
invocations of a continuation inside those blocks would make absolutely
no sense...that is, unless I could guarantee that at most one
continuation reached the ensure block. Which essentially means that I'd
want to have a continuation disabled once any invocation reached a
certain level in the stack. Perhaps a Continuation that would be
expired after the current context completes would be useful? Consider this:
self one.
[Continuation currentDo: [ :cc | ^cc ].
self two]
ensure: [self three].
We could conceivably constuct a continuation such that once any
invocation of the continuation proceeds beyond the initial context,
other invocations would be disallowed. We could further restrict the
continuation such that one and only one continuation could be active at
any given time.
>So the general gist of those papers is to find a way such that B gets
>evaluated exactly once, when you promise that you're truly done with A -
>or, alternatively, to invalidate any continuations you've captured inside
>A as soon as you evaluate B.
>
>It's *not* as simple as just always evaluating B whenever you leave A (for
>example by invoking another continuation).
>
>In a Seaside context, my recommendation is simply this: *do not show a
>page to a user from within a block that is protected by #ensure:*. "[self
>call: Foo] ensure: [...]" is just bad news.
>
>
>
>>Btw, what about changing the protocol of continuations from #value to
>>#resume (I think the arguments in favor of this change were that there
>>is more of an expectation of non-normal flow control happening with
>>#resume)?
>>
>>
>
>For now it's too useful that they be polymorphic with blocks - I guess it
>could have both #resume and #value...
>
Or you could simply wrap the continuation in a block or message send.
To me, having a continuation understand #value is like having a Process
(or a stack) understand #value. It obscures the true intention. If you
really need the convenience, why not implement the following method on
Continuation:
asValuable
^[self resume]
or, even better (esp without block closures):
asValuable
^MessageSend receiver: self selector: #resume
>> Also, I'd like to see another method on a continuation that
>>will create and schedule another process to resume a continuation (and
>>leave the current stack alone)...maybe that's already there though.
>>Something like:
>>
>>fork
>>
>> [self resume] fork
>>
>>
>
>You've got to be careful here - the Continuation class that comes with
>Seaside is not thread safe, and it's not trivial to write one that is
>(although if Anthony's "context tags" made it ito 3.6, that makes it
>easier).
>
>
Could you explain that a little more. Do you mean that a continuation
cannot be concurrently invoked? I suspect that's the case since it
doesn't look like you're copying the context objects in #restoreValues
...btw, didn't you copy the stack in previous versions? It doesn't seem
like that should be so difficult to either prevent, or accommodate.
>What's the common use you see for that?
>
I just don't like the fact that sending #value to a Continuation trashes
the calling stack (with no unwind)...it seems like that shouldn't be
necessary.
>>One of these days, we'll have to think about reconciling continuations
>>and exception handling (where an exception would hold a continuation and
>>a handler would run in a different branch off the same stack as the
>>continuation (instead of being lopped onto the bottom of the stack)).
>>
>>
>
>One of these days. ;)
>
- Stephen
More information about the Seaside
mailing list