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>    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.