Path: utzoo!attcan!uunet!cs.utexas.edu!sdd.hp.com!ucsd!ucbvax!hplabs!hpda!hpcuhc!hpsemc!jmorris From: jmorris@hpsemc.HP.COM (John V. Morris) Newsgroups: comp.sys.hp Subject: Re: HPPA Context switching code - still having problems Message-ID: <1250035@hpsemc.HP.COM> Date: 2 Jul 90 19:09:10 GMT References: <7748@ccncsu.ColoState.EDU> Organization: HP Technology Access Center, Cupertino, CA Lines: 258 Here's some code for working with coroutines. I've avoided the issues of knowing which registers need saving by invoking setjmp/longjmp to save and restore them. John Morris HP VAB Partners Lab (408)725-3871 -------------------------------- Cut Here ---------------------------- #! /bin/sh # This is a shell archive. Remove anything before this line, # then unwrap it by saving it in a file and typing "sh file". # # Wrapped by jmorris at hpsemc on Mon Jul 2 11:58:20 1990 # Contents: # Makefile coroutine.s test.c PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:$PATH; export PATH echo 'At the end, you should see the message "End of shell archive."' echo Extracting Makefile sed 's/^@//' >Makefile <<'@//E*O*F Makefile//' all: coroutine.o test test: test.o coroutine.o cc -g test.o coroutine.o -o test @.s.o: cc -c $*.s @//E*O*F Makefile// set `wc -lwc coroutine.s <<'@//E*O*F coroutine.s//' ;************************************************************************* ; ; #include ; char stack[STACKSIZE]; ; jmp_buf env; ; jmp_buf oldenv, newenv; ; ; coroutine(env, routine, param, stack, STACKSIZE); ; ; resume(oldenv, newenv) ; ; Coroutine() initializes a new co-routine. ; Co-routines are very similar to processes, but they all take place ; inside one UNIX process. Since the operating system is not involved, ; the context switch time ("resume" time) is very short. ; ; Resume() switches from one co-routine to another. The current context ; is saved in oldenv, and the new context is loaded from newenv. ; ; When a co-routine is activated the first time, the specified procedure ; will be invoked with the specified parameter. The procedure should ; not return. (It should resume to another coroutine, or exit). ; ; History ; 900702 John Morris Using sigsetjmp() and siglongjmp(), the POSIX routines. ; ; 891031 John Morris Changed call to $$dyncall to match conventions. ; Linker was blindly replacing the bl $$dyncall,2 ; with ble $$dyncall, and return address was messed up. ; ; 880506 John Morris Changed name to "coroutine" and included the resume ; function in same file. Also, added parameter that ; gets passed to coroutine the first time it is invoked. ; Use $$dyncall to do dynamic subroutine call instead ; of ble instruction. ; ; 870000 John Morris Originally written to satisfy various customer's needs ; at the HP Technology Access Center. ;************************************************************************** ; Should the signal mask be saved as part of the context? #define SAVEMASK 0 /* 0 means don't save it, 1 means save it */ .code #define env arg0 #define routine arg1 #define param arg2 #define stack arg3 #define size r31 #define temp r31 .import sigsetjmp .import siglongjmp .import $$dyncall ; void coroutine(env, routine, param, stack, size); ;************************************************************************ ; coroutine creates a 'setjmp' environment with a new stack ;************************************************************************ coroutine .proc .callinfo caller, save_rp .export coroutine .enter ; Note that 'size' is not actually used by this routine. It is necessary ; if equivalent routines are to be implemented on other architectures ; (for example, if stack grows down instead of up). ; align the new stack area to an 8 byte boundary and allocate two frames ; the frames will be referred to as 'current' and 'next' addi 103, stack, stack ; add 7 to round up and add 96 for two frames. depi 0, 31, 3, stack ; clear the low 3 bits, effect is to round up. ; save the local variables in the current stack frame stw r0, -68(stack) ; set a zero return address so debugger trace stops stw sp, -48(stack) ; save the current stack pointer so it can be restored stw routine, -88(stack) ; save the routine address to invoke later on stw param, -84(stack) ; save the parameter to pass on ; switch to the new stack and save the environment copy stack,sp .call bl sigsetjmp, rp ldi SAVEMASK, arg1 ; if we return to this point via 'longjmp' (ie. return value is non-zer0) ... if1 comb,=,n ret0, r0, else1 ; if return value is zero, skip ahead ; ... then call the routine with the argument given to 'longjmp' ldw -88(sp), r22 ; get the procedure address from stack ldw -84(sp), arg0 ; get the parameter we saved in the stack .call bl $$dyncall, 31 ; invoke the procedure copy 31, rp ; (Note: linker substitutes ble for bl) ; note: the routine should not return break ; otherwise, restore the stack pointer and do a normal return else1 ldw -48(sp), sp .leave endif1 .procend ; Note: resume() could be written in C, but it is included here in ; assembly in order to keep the two routines together. ; if (sigsetjmp(oldenv, SAVEMASK) == 0) ; siglongjmp(newenv,1); ; resume(oldenv, newenv) ;******************************************************************** ; resume switches from one coroutine to another ;******************************************************************** .proc .callinfo caller, frame=0, entry_gr=3, save_rp .export resume, entry resume .enter ; save the current context with sigsetjmp copy arg1, r3 ; save pointer to newenv bl sigsetjmp, rp ldi SAVEMASK, arg1 ; if return value is zero, invoke the new co-routine ; (otherwise, another coroutine just invoked us, so just return) if2 comib,<>,n 0, ret0, endif2 ; see if return value was zero bl siglongjmp, rp ; if so, invoke longjmp copy r3, arg0 ; ... after restoring the parameter endif2 ; done .leave .procend .end @//E*O*F coroutine.s// set `wc -lwc test.c <<'@//E*O*F test.c//' /****************************************************************** Test program for coroutines. Creates a bunch of coroutines, and then switches among them **********************************************************************/ #include #define COUNT 10 typedef char stack_type[50000]; /* allocate a stack and a context for each coroutine */ /* (also, allocate an extra context for the main program) */ jmp_buf env[COUNT+1]; stack_type stack[COUNT]; main() { int i, proc(); /* create the coroutines */ for (i = 0; i