Path: utzoo!attcan!uunet!lll-winken!lll-tis!ames!pasteur!ucbvax!decwrl!megatest!djones From: djones@megatest.UUCP (Dave Jones) Newsgroups: comp.unix.wizards Subject: Re: alloca... (and setjmp/longjmp coroutines) Message-ID: <599@goofy.megatest.UUCP> Date: 11 Jun 88 02:18:31 GMT References: <11902@mimsy.UUCP> Organization: Megatest Corporation, San Jose, Ca Lines: 129 From article <11902@mimsy.UUCP>, by chris@mimsy.UUCP (Chris Torek): > In article <16126@brl-adm.ARPA> ted%nmsu.csnet@relay.cs.net writes: > [alloca + setjmp/longjmp for coroutines] > > longjmp is not suitable for coroutines because it is valid for longjmp > to attempt to unwind the stack in order to find the corresponding > setjmp, and it is therefore legal for longjmp to abort if it is > attempting to jump the `wrong way' on the stack. > -- > In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) > Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris I'm no C wizard -- BSD4.2 and Sun3-OS are the only C's I've ever used -- but it seems to me that longjmp is the most suitable technique going, by default. What else is there? You could use [abuse?] sigvec and kill. But if you use separate stacks of fixed sizes, they can overflow with disastrous consequences. And -- correct me if I'm wrong -- more systems have setjmp/longjmp/alloca than have sigvec and kill. Or you could use a smattering of assembler. But, it will certainly run on more kinds of machines if written in C than it would if written in assembler. And to answer your objection about unwinding the stack, you can see to it that the stack is restored _before_ you do the longjmp, so the longjmp can "unwind" as it pleases. I recently wrote a little discrete-event-simulator using setjmp/longjmp/alloca to do lightweight processes. We hope to run it on both Sun3s and IBM PCs. Haven't tried it on the PCs yet, so I don't know if it works there or not. Do I have a surprise in store for me? Here's what I did. The main simulator loop calls alloca(1) to find the bottom of the part of the stack that lightweight processes will be using. It squirrels that address away in the variable {stack_bottom}. To start a process initially, it just calls the process's procedure. Then the simulator and the process trade setjmp/longjmp cycles through a couple of jmpbufs. Well, you'll see. Is there some gotcha that will break this code on some systems? If so, is there a better [more machine independent] way? /*********************************************************************** ** Run the simulation, stopping after some number of ticks, unless ** all processes exit, or some process calls PSim_stop() first. ***********************************************************************/ unsigned long PSim_run(obj, ticks) Simulation* obj; unsigned long ticks; { obj->stack_bottom = (char*)alloca(1); obj->stop_time += ticks; while(!obj->quit) { /* Get a busy process from the busy-queue */ obj->active = (Process*) PQ_pop(&obj->busy); /* If all processes are finished, or are waiting on ** a semaphore, we are blocked, and must exit the simulation. */ if(obj->active==0) goto end_simulation; { register Process *active = obj->active; /* Update the time to the time of the active process */ obj->time = active->busy_until; if( obj->time >= obj->stop_time) goto end_simulation; if(setjmp(active->suspend) == 0) if(active->stack_save == 0) /* Process has not yet started. Call its start-procedure. */ active->return_value = (*(active->start))(obj); else { /* Process has been suspended, and will now be restarted. */ /* allocate the restarting process's stack. */ alloca( active->stack_size ); /* restore it */ bcopy( active->stack_save, active->stack_real, active->stack_size); sfree(active->stack_save); active->stack_save = 0; /* restart the process */ longjmp(active->restart, 1); } } } end_simulation: cleanup(obj); return obj->time; } static suspend_active_proc(obj) register Simulation* obj; { char* stack_top = (char*)alloca(1); long size = abs(obj->stack_bottom - stack_top); register Process* active = obj->active; active->stack_save = (char*)smalloc(size); active->stack_real = min(stack_top, obj->stack_bottom); active->stack_size = size; if(setjmp(active->restart) == 0) { /* copy the stack and return to the simulator. */ bcopy( active->stack_real, active->stack_save, size); longjmp(active->suspend, 1); } }