Path: utzoo!attcan!uunet!mcsun!sunic!enea!jerker From: jerker@enea.se (Jerker W}gberg) Newsgroups: comp.lang.c Subject: Longjmping back, and back again; Coroutines in C Message-ID: <457@enea.se> Date: 17 Nov 89 21:06:30 GMT Reply-To: jerker@helios.se (Jerker W}gberg) Organization: Enea Data AB, Sweden Lines: 134 UUCP-Path: mcvax!kth!sunic!helios!jerker I am trying to implement coroutines using plain C. My application is not time critical, so there is no need for speed, being portable is far more important. It would be great if there was a way to switch stacks of the processes using just C. I have figured out a way to implement this that works fine on a PC with MSC 5.1, but wreaks havoc when run on a SUN4. The idea is that instead of actually switching stacks, I use the "real" stack but swap it in and out of malloced memory. 1. Can somebody explain why this fails on a SUN4. This is the first program that I ever have tried on a SUN4 so I don't have any idea what happens inside the CPU. Could it be that the SUN4 have some peculiar registers that must be restored but isn't below? 2. Are there more machines out there that probably will throw up when executing this code, apart from CPU's where the stack is growing upward instead of downward. This can be taken care of, but I did not want to clutter the example below. 3. Does anyone know of a better way to do coroutines in C ? Like to test it yourself ? Here is a barebones version: ------------------------------ cut here ------------------------------------- /* ** Test of coroutines. ** Outputs : ** func1 ** func2 ** func1 ** . ** . ** ad inf */ #include #include #include char *stack_bottom; jmp_buf main_buf; int cc; /* Current coroutine */ func_1() { int foo[50]; /* To get different stack depths at dispatch. If the */ /* stack depths are equal at dispatch the program */ /* just continues with func2 on my SUN4. If stacks */ /* are unequal, I get segmentation fault. */ for (;;) { printf("func1\n"); dispatch(); } } func_2() { for (;;) { printf("func2\n"); dispatch(); } } struct sav /* Control block for each coroutine */ { char *stack; /* Pointer to saved stack */ int stack_len; /* Lenght of stack */ int (*func)(); /* Pointer to each coroutines start address */ jmp_buf retbuf; /* SP and PC of each coroutine */ } sav[2] = { {NULL, 0, func_1} , {NULL, 0, func_2} }; main() { char stack_mark; stack_bottom = &stack_mark; /* ** Here we go. Run a coroutine until it dispatches then ** run the other. */ for (;;cc ^= 1) { if (setjmp(main_buf) == 0) if (sav[cc].stack == NULL) sav[cc].func(); /* First time */ else longjmp(sav[cc].retbuf, 1); /* Continuation */ } } dispatch() { char stack_mark; jmp_buf disp_buf; if (setjmp(disp_buf) == 0) { /* ** Save the jmp_buf so that we can return. ** Save the stack into mallocated memory and ** longjmp back to main */ memcpy(sav[cc].retbuf, disp_buf, sizeof(jmp_buf)); sav[cc].stack_len = stack_bottom - &stack_mark; sav[cc].stack = malloc(sav[cc].stack_len); memcpy(sav[cc].stack, &stack_mark, sav[cc].stack_len); longjmp(main_buf, 1); } else { /* ** The longjmp from main set SP ok but there is just ** garbage from the previous coroutine on the stack. Copy ** the stack from previous run and return to caller of ** dispatch. */ memcpy(&stack_mark, sav[cc].stack, sav[cc].stack_len); free(sav[cc].stack); } }