continuations and threads Jim Blandy 18 Feb 2000 00:39 UTC
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.