Re: continuations and threads
Jim Blandy 25 Feb 2000 21:34 UTC
Jim> Now that you mention it, there is something I remember Roland McGrath
Jim> implementing for SMLNJ, while working for Olin Shivers.
Jim> I think the basic idea was that frames (activation records) for ML
Jim> functions were kept on the heap (as normal), while frames for C
Jim> functions were kept on a C stack. You let the ML frames behave like
Jim> the first-class objects they are --- you can call/cc to your heart's
Jim> content --- but if you try to return to a C frame in a non-stacklike
Jim> order, the ML/C boundary code detects this and throws an exception.
Jim> I think he even arranged it so that if you returned early to an older
Jim> C frame, the younger frames would be popped appropriately (via
Jim> longjmp), and any references to younger C frames would be marked
Jim> invalid, so you'd get an error if you tried to use them.
Mike> The C FFI Richard Kelsey and I implemented for Scheme 48 does this as
Mike> well. Here's the relevant blurb from the documentation:
Mike>
Mike> There are some complications that occur when mixing calls from C to
Mike> Scheme with continuations and threads. C only supports downward
Mike> continuations (via longjmp()). Scheme continuations that capture a
Mike> portion of the C stack have to follow the same restriction. For
Mike> example, suppose Scheme procedure s0 captures continuation a and
Mike> then calls C procedure c0, which in turn calls Scheme procedure s1.
Mike> Procedure s1 can safely call the continuation a, because that is a
Mike> downward use. When a is called Scheme 48 will remove the portion of
Mike> the C stack used by the call to c0. On the other hand, if s1
Mike> captures a continuation, that continuation cannot be used from s0,
Mike> because by the time control returns to s0 the C stack used by c0
Mike> will no longer be valid. An attempt to invoke an upward
Mike> continuation that is closed over a portion of the C stack will
Mike> raise an exception.
Actually, this is less powerful than what I described. In Roland's
system, you don't need to unwind the C stack when s1 invokes a. You
only need to unwind the C stack when s0 returns. If s0 instead
invokes some continuation b captured by s1, that's fine.
The key observation here is that the only necessary connection between
C frames and Scheme frames occurs when control crosses a Scheme/C or
C/Scheme boundary. Each C frame may be exited only once, and you may
only return to a C frame after popping all younger C frames. But
beyond those rules, there's no necessary restriction on the way Scheme
frames may behave.