115 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			115 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|   (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | |
|   See the copyright notice in the ACK home directory, in the file "Copyright".
 | |
| */
 | |
| 
 | |
| /*
 | |
|   Module:	SYSTEM
 | |
|   Author:	Ceriel J.H. Jacobs
 | |
|   Version:	$Id$
 | |
| */
 | |
| 
 | |
| /*
 | |
|     An implementation of the Modula-2 NEWPROCESS and TRANSFER facilities
 | |
|     using the topsize, topsave, and topload facilities.
 | |
|     For each coroutine, a proc structure is built. For the main routine,
 | |
|     a static space is declared to save its stack. For the other coroutines,
 | |
|     the user specifies this space.
 | |
| */
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include "libm2.h"
 | |
| #include <m2_traps.h>
 | |
| 
 | |
| #define MAXMAIN 2048
 | |
| 
 | |
| static struct proc mainproc[MAXMAIN / sizeof(struct proc) + 1];
 | |
| 
 | |
| static struct proc* curproc = 0; /* current coroutine */
 | |
| extern char* MainLB; /* stack break of main routine */
 | |
| 
 | |
| void _SYSTEM__NEWPROCESS(
 | |
|     int (*p)(void) /* coroutine procedure */,
 | |
|     struct proc* a /* pointer to area for saved stack-frame */,
 | |
|     unsigned int n /* size of this area */,
 | |
|     struct proc** p1 /* where to leave coroutine descriptor,
 | |
|                    in this implementation the address of
 | |
|                    the area for saved stack-frame(s) */
 | |
|     )
 | |
| {
 | |
| 	/*	This procedure creates a new coroutine, but does not
 | |
| 	    transfer control to it. The routine "topsize" will compute the
 | |
| 	    stack break, which will be the local base of this routine.
 | |
| 	    Notice that we can do this because we do not need the stack
 | |
| 	    above this point for this coroutine. In Modula-2, coroutines
 | |
| 	    must be level 0 procedures without parameters.
 | |
| 	*/
 | |
| 	char* brk = 0;
 | |
| 	unsigned sz = topsize(&brk);
 | |
| 
 | |
| 	if (sz + sizeof(struct proc) > n)
 | |
| 	{
 | |
| 		/* not enough space */
 | |
| 		TRP(M2_TOOLARGE);
 | |
| 	}
 | |
| 	a->size = n;
 | |
| 	a->proc = p;
 | |
| 	a->brk = brk;
 | |
| 	*p1 = a;
 | |
| 	if (topsave(brk, a + 1))
 | |
| 		/* stack frame saved; now just return */
 | |
| 		;
 | |
| 	else
 | |
| 	{
 | |
| 		/* We get here through the first transfer to the coroutine
 | |
| 		   created above.
 | |
| 		   This also means that curproc is now set to this coroutine.
 | |
| 		   We cannot trust the parameters anymore.
 | |
| 		   Just call the coroutine procedure.
 | |
| 		*/
 | |
| 		(*(curproc->proc))();
 | |
| 		_cleanup();
 | |
| 		_exit(0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void _SYSTEM__TRANSFER(struct proc** a, struct proc** b)
 | |
| {
 | |
| 	/*	transfer from one coroutine to another, saving the current
 | |
| 	    descriptor in the space indicated by "a", and transfering to
 | |
| 	    the coroutine in descriptor "b".
 | |
| 	*/
 | |
| 	unsigned size;
 | |
| 
 | |
| 	if (!curproc)
 | |
| 	{
 | |
| 		/* the current coroutine is the main process;
 | |
| 		   initialize a coroutine descriptor for it ...
 | |
| 		*/
 | |
| 		mainproc[0].brk = MainLB;
 | |
| 		mainproc[0].size = sizeof(mainproc);
 | |
| 		curproc = &mainproc[0];
 | |
| 	}
 | |
| 	*a = curproc; /* save current descriptor in "a" */
 | |
| 	if (*b == curproc)
 | |
| 	{
 | |
| 		/* transfer to itself is a no-op */
 | |
| 		return;
 | |
| 	}
 | |
| 	size = topsize(&(curproc->brk));
 | |
| 	if (size + sizeof(struct proc) > curproc->size)
 | |
| 	{
 | |
| 		TRP(M2_TOOLARGE);
 | |
| 	}
 | |
| 	if (topsave(curproc->brk, curproc + 1))
 | |
| 	{
 | |
| 		/* stack top saved. Now restore context of target
 | |
| 		   coroutine
 | |
| 		*/
 | |
| 		curproc = *b;
 | |
| 		topload(curproc + 1);
 | |
| 		/* we never get here ... */
 | |
| 	}
 | |
| 	/* but we do get here, when a transfer is done to the coroutine in "a".
 | |
| 	*/
 | |
| }
 |