Subject: Re: call/cc
From: rpw3@rigden.engr.sgi.com (Rob Warnock)
Date: 1996/10/12
Newsgroups: comp.lang.scheme
Message-ID: <53njfl$909@tokyo.engr.sgi.com>

Dan Vigeland  <dan.vigeland@ilf.uio.no> wrote:
+---------------
| I am looking for a call-with-current-continuatin (call/cc among friends)
| eqvivalent in any other languages. Has anybody heard about such?
+---------------

In many C environments (and in many Unix environments, but certainly not all)
it is possible for a user stack to be allocated from the heap, and then
(usually) setjmp/longjmp can get used as a second-class call/cc. I say
"second-class call/cc" and not just call-with-escaping-continuation
(a.k.a. call/ec) because if the stack hasn't unwound past the point at
which you did the setjmp you *can* call the same "continuation" more than
once, although you need to be very careful.

[I'm assuming here that longjmp isn't being implemented by stack-copying,
in which case it really wouldn't matter whether the stack had unwound past
the point of the setjmp. And if all you want *is* call/ec, then setjmp/longjmp
alone (without the stack/heap diddling) are quite adequate for that.]

For example, I have done this on several Unix boxes (some entirely in C,
some needed a little dab of assembler glue) to create user-mode coroutines,
which is all you need to give you "processes".

	proc_p
	new_process(int stack_size, char name[], void (*func)(), int arg2, ...)
	{
		...malloc some space...
		...make it look like a stack...
		...do a setjmp into a "save area" in said stack...
		...patch the save area (jmp_buf) so that any args passed
		   in registers are correct, and copy overflow args into
		   the right places on the stack...
		...patch the save area to the "return PC" is actually the
		   beginning location of "func"...
		...return the malloc'd object...
	}

You "resume" such a "process" (or "thread") -- while suspending yourself --
as follows [actual working C code, at least on SGI/MIPS platforms]:

	void resume(proc_p p)
	{
		external proc_p self;
		jmp_buf regs;

		if (setjmp(regs))
			return; /* we've just been resume'd again */

		/* save pointer to our state */
		self->save_area = (long *)&regs[0];

		/* switch identities */
		self = p;

		/* wake our new self */
		longjmp(*(jmp_buf*)self->save_area, 1);
		/*NOTREACHED*/
	}

In Scheme, I think this would (roughly) be:

	(define (resume new)
	  (call/cc (lambda (old)
		     (self 'set-saved-k! old)	; postulate some "object" style
		     (set! self new)
		     ((self 'get-saved-k) 'dont-care))))

Of course, if the caller of "resume" is ever to run again, it must have
saved the global "self" on a run queue or wait queue somewhere first
before overwriting it.

Another reason for calling setjmp/longjmp *2nd-class* call/cc is that even
though you can do "process" switching and escape-continuations with them,
there are plenty of other patterns of using call/cc which *can't* be done
with setjmp/longjmp...


-Rob

-----
Rob Warnock, 7L-551		rpw3@sgi.com
Silicon Graphics, Inc.		http://reality.sgi.com/rpw3/
2011 N. Shoreline Blvd.		Phone: 415-933-1673  FAX: 415-933-0979
Mountain View, CA  94043	PP-ASEL-IA