I think it's worth noting that most implementations of threads have an
unfortunate interaction with some implementations of continuations,
notably that used in Guile and SCM.
In this particular implementation, call/cc actually copies the C stack
into the heap, and applying a continuation copies the saved stack from
the heap back onto the C stack. There are a number of tricks here:
- We use the address of some local C variable in our `main' function
as the "base" of the stack. It doesn't really matter where it is,
as long as it's below all the state we need to capture. The C
function that does call/cc's dirty work uses the address of one of
its own local C variables as the other end of the stack.
- The interpreter keeps the values of all Scheme variables in the
heap, never in local C variables. This assures that the
continuation only carries references to variables, not variable
values itself, so continuations can share variables appropriately.
- When we apply a continuation, the continuation's saved stack may be
larger than our current stack. So we recurse until we've grown our
stack large enough to hold the continuation's saved data, copy the
continuation back over our stack, and then longjmp to it.
I think this is pretty gross, but it does allow a natural
correspondence between C function calls and Scheme function calls:
continuations preserve active calls to C functions, as well as Scheme
functions. If it's important to your users to be able to write C
functions that can call and be called by Scheme functions, I think
this is a plausible implementation choice. I'm not aware of any other
implementation that does this while requiring so little distortion of
your C code. (But if you know of one, I'd love to hear about it!)
It's also proven to be surprisingly portable.
However, the hack only works because you copy the stack back to the
same address it came from. We can treat the stack as an opaque block
of bytes, ignoring all the frame structure, intra-stack pointers,
etc. exclusively because we use it only at the address where it was
built.
So, if each thread's stack lives at a different address, this means
that continuations captured by one thread cannot be used by any other
thread. To do so would require using a copy of one thread's stack at
another thread's stack address; we lack the information about the
stack's real structure needed to relocate it for use at a new address.
Each continuation can only be used by the thread that captured it.
Certainly, it has never been The Scheme Way to cripple a nice
interface to accomodate a particular implementation strategy ---
especially one as twisted as this one --- so I'm not suggesting that
Marc should change the SRFI. However, I think this problem will be
common to Scheme systems intended to work closely with C code, so I
thought I'd mention it.