alloca... (and setjmp/longjmp coroutines)

Dave Jones djones at megatest.UUCP
Sat Jun 11 12:18:31 AEST 1988


>From article <11902 at mimsy.UUCP>, by chris at mimsy.UUCP (Chris Torek):
> In article <16126 at brl-adm.ARPA> ted%nmsu.csnet at 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 at 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);
    }
}



More information about the Comp.unix.wizards mailing list