Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!mimsy!oddjob!uwvax!husc6!ut-sally!utah-cs!utah-gr!spline!thomas From: thomas%spline.uucp@utah-gr.UUCP (Spencer W. Thomas) Newsgroups: comp.unix.wizards Subject: Re: A very interesting problem -- multiprocessing Message-ID: <2017@utah-gr.UUCP> Date: Mon, 4-May-87 07:15:25 EDT Article-I.D.: utah-gr.2017 Posted: Mon May 4 07:15:25 1987 Date-Received: Tue, 5-May-87 01:32:05 EDT References: <7176@brl-adm.ARPA> <4589@sci.UUCP> Sender: news@utah-gr.UUCP Reply-To: thomas%spline.UUCP@utah-gr.UUCP (Spencer W. Thomas) Organization: Univ of Utah CS Dept Lines: 138 Funny you should ask: you could do this on V6, through a special coroutine library. Unfortunately, this was not very portable (depended on PDP-11 architecture, I think), so out the window it went (was it in V7? I don't remember.) Anyway, if you want to coroutine a couple of functions with no arguments (easiest case), you just allocate a "large enough" chunk of memory for each one's stack, set the stack pointer to the correct end of it (top on a Vax), and call the function. (Oh, I should mention that you need to save the real stack pointer somewhere.) The "Suspend" action then saves the co-routine stack pointer in a static variable, and resumes execution on the real stack. To continue a co-routine, you restore its saved stack pointer and do a subroutine return. Maybe a little code fragment would help: static char * real_sp; /* Save real stack pointer */ static int num_coroutines = 0; /* None initially */ static char * save_co_sp[MAX_CORTN]; /* stack pointer storage */ static int cur_coroutine = -1; /* Currently executing coroutine */ /* Start a co-routine function. Returns (via coroutinepause) an * integer identifier for that function. */ /* All local vars must be register. Don't want any on stack, will screw up return from coroutinepause. */ coroutinestart( fn, stack_size ) register void (*fn)(); register int stack_size; { register char * stack; if ( cur_coroutine >= 0 ) error( ... ) /* Can't start co-routine from a co-routine */ if ( ++num_coroutines == MAX_CORTN ) error( ... ) /* core dump or something */ stack = malloc( stack_size ); stack += stack_size; /* Assume stack grows as --sp */ cur_coroutine = num_coroutines - 1; /* Now go into some fancy assembly code that saves the current stack and invokes the co-routine. Example is pseudo-vax */ asm( "mov sp,real_sp" ); asm( "mov stack,sp" ); (*fn)(); /* Should never return, but in case ... */ asm( "0" ); /* Or something else illegal */ /* Could do something reasonable here if we had the concept of a "dead" coroutine, but we don't. */ } /* Called from a coroutine to relinquish control */ /* Note that this will return to the fn that called coroutinestart or coroutineresume. A return from here occurs via coroutineresume. (Confused yet?) */ coroutinepause() { register char * stack; register int last_cortn; if ( cur_coroutine < 0 ) return; /* Paranoid check */ /* More assembly to get back to regular stack */ asm( "mov sp,stack" ); asm( "mov real_sp,sp" ); /* Save costack pointer */ save_co_sp[cur_coroutine] = stack; last_cortn = cur_coroutine; cur_coroutine = -1; return last_cortn; } /* Called from normal code to resume a coroutine. Also returns via coroutinepause. Returns id. All locals must be register. */ coroutineresume( id ) register int id; { register char * stack; if ( id < 0 || id >= num_coroutines || cur_coroutine >= 0 ) return id; /* Non-existant co-routine, or already active */ stack = save_co_sp[id]; cur_coroutine = id; asm( "mov sp, real_sp" ); asm( "mov stack,sp" ); return; /* from call to coroutinepause */ } /* Switch from one coroutine to another */ coroutineswitch( id ) register int id; { register char * stack; /* Sanity check id, ensure co-routine active */ if ( id < 0 || id >= num_coroutines || cur_coroutine < 0 ) return id; /* Switch stacks */ asm( "mov sp, stack" ); save_co_sp[cur_coroutine] = stack; stack = save_co_sp[id]; cur_coroutine = id; asm( "mov stack,sp" ); return; } Each co-routine should look like this: a_co_rtn() { coroutinepause(); for (;;) { /* Do some work */ coroutinepause(); /* Do some more work, maybe switch to another */ coroutineswitch( other_id ); /* etc */ } /* NOTREACHED */ } The initial call to coroutinepause() is for two reasons: it permits the function id to be recorded somewhere meaningful for the application, and it gets the coroutine stack pointer set up. You have to be careful not to exceed your allocated stack space, the computer will not check it for you! You really can't expand the stack once you start, either. I imagine there is not more than one bug per line of code here, but the ideas should be right. There is no doubt potential for combining common code, and eliminating the special cases. You should be able to start a coroutine from another, and so on. (E.g., if resumecoroutine is called from a coroutine, just do the coroutineswitch case. I wanted to separate function for clarity.) This code is NOT tested. Have fun with it. =Spencer ({ihnp4,decvax}!utah-cs!thomas, thomas@cs.utah.edu)