Article 149499 of comp.os.vms: Last week i tried to implement MODULA2 coroutines on VMS/VAX by using setjmp/longjmp and get that strange fault at runtime. The VAX C library (and probably the DEC C library) uses uses the VMS condition signaling mechanism (not to be confused with Unix signals!) to implement longjmp. Consider what needs to happen to execute a longjmp(): 1. The stack must be unwound to the stack frame that was current at the point that setjmp() was executed; 2. Control must be transfered to the appropriate point, as saved in the jump buffer by setjmp(). Many C implementations implement (1) trivially - they simply change the stack pointer, immediately discarding all intermediate frames. This would not be acceptable under VMS since there can be condition handlers declared by some of those intermediate frames, and the VMS procedure calling standard requires that they be executed. Fortunately, VMS already provides a means to accomplish (1) through the signal handling mechanism. The C RTL's implement longjmp() in a couple of steps: (1) The C RTL declares a signal handler way up at the top of the stack. (2) longjmp() signals condition LONGJMP. No user-defined handler is supposed to attempt to handle this condition. (3) The LONGJMP condition propagates all the way up the stack to the C RTL handler. It grabs it, looks at jump buffer (which was presumably left at some known place by longjmp()), and uses $UNWIND to properly unwind the stack to the appropriate frame (giving any intermediate handlers the opportunity to respond to the "unwinding" condition that they will be invoked with). (4) Finally, it causes control to be transfered to the appropriate point. (It actually does this as part of the $UNWIND call.) The details get more complicated, though I can't reconstruct them at the moment. But this is the basic idea. The main idea is that coroutine is a co-process that has separate context(stack and registers) and commonn with others co-routines global variables. Coroutine is not scheduled by kernel - control flow is switched by special procedure. And me, as a modula2 compiler developer, tried to implement this proceedure. On ALL the others platforms we did setjmp, patch the stack-pointer(r13 in this case?) with separate coroutine stack, and then we did longjmp on patched jmp_buf. And for VMS/VAX it says %SUBJ%. This approach cannot work on VMS. Because of the way the implementation is done, a longjmp() can only work if actually directed to a frame "further up the stack". (Note that what you are attempting to do is most definitely *not* supported by the ANSI definition of setjmp() and longjmp(). It happens to work on other implementations, which don't have as rich a procedure call semantics, but on VMS - forget it.) I have absolutely no idea what kind of fault may be cause by such a stupid procedure as longjmp at all. Can anyone help me? I would greatly appreciate it! BTW, normal usage of setjmp/longjmp works fine, we did implement MODULA2 exceptions in this way and they work. HOW can that longjmp distinguish one case from another? It's unbelievable! Coroutine's stack was allocated both on main stack and as a static variable-buffer - it makes no difference. What's probably happening is that your new stack starts with a frame with no backward frame pointer, and no condition handler. When longjmp() signals LONGJMP, VMS's search for a handler works up frame by frame until it reaches the top of your allocated stack, at which point it calls the last-chance handler, which reports the error you see and, since LONGJMP is signalled with a Fatal severity (since the program mustn't continue by returning from the longjmp() call whatever may go wrong!), terminates your image. You *might* be able to fake things out by making sure top frame of each stack you create points back to some frame on the system stack. However, at least on VAX'es, it's really much simpler to just write a few lines of MACRO code and do exactly what you need, rather than playing around trying to sneak things past some code that is doing its best to make sure you "play by the rules". Attached below is an example of such code. I extracted this from a large system, so it's full of stuff specific to that system - but you should be able to get the ideas from here. Note: On an Alpha, this probably isn't quite as easy! -- Jerry .TITLE TASKSUPPORT - Support routines for tasking .IDENT 'V01-000' ; EDITLEVEL=08 .PSECT SUPPORT_CODE,CON,EXE,GBL,PIC,SHR,RD,REL,NOWRT .SUBTITLE Get caller's FP ; ; FRAME * ; GetFP() ; ; Return to caller the the value of his frame pointer. .ENTRY GetFP,^M<> MOVL 12(FP),R0 ;R0 <- saved FP RET ;Easy enough .SUBTITLE Tasking support - swap tasks ; ; This function is used only in conjunction with the tasking code. ; ; The TASK datatype: ; struct task ; { RQUEUE rqueue; /* Links */ ; RQUEUE msgs; /* Pending messages */ ; LONG flags; ; #define TASK$V_INIT 0 /* Task is initialized */ ; #define TASK_INIT BITVAL(TASK$V_INIT) ; #define TASK$V_ASLEEP 1 /* Task is asleep */ ; #define TASK_ASLEEP BITVAL(TASK$V_ASLEEP) ; #define TASK$V_TIMEDOUT \ ; 2 /* Task timeout completed */ ; #define TASK_TIMEDOUT \ ; BITVAL(TASK$V_TIMEDOUT) ; FRAME *FPsave; /* Saved Frame Pointer */ ; void (*func)(); /* Initial function */ ; ADDR alist; /* Initial argument list */ ; ADDR stack; /* Task's stack */ .PSECT TASK,ABS next: .BLKL ;Forward (relative) queue link last: .BLKL ;Backward (relative) queue link mnext: .BLKL ;Forward (relative) queue link for msgs mlast: .BLKL ;Backward (relative) queue link for msgs flags: .BLKL ;Flags TASK$V_INIT = 0 ;Task is initialized TASK_INIT = 1@TASK$V_INIT TASK$V_ASLEEP = 1 ;Task is asleep TASK_ASLEEP = 1@TASK$V_ASLEEP TASK$V_TIMEDOUT = 2 ;Task timeout completed TASK_TIMEDOUT = 1@TASK$V_TIMEDOUT FPsave: .BLKA ;Saved Frame Pointer func: .BLKA ;Initial function alist: .BLKA ;Initial argument list stack: .BLKA ;Process stack old = 4 ;Old state (to be saved) new = 8 ;New state (to be started) .EXTERNAL TaskExit .PSECT SUPPORT_CODE,CON,EXE,GBL,PIC,SHR,RD,REL,NOWRT ; ; void ; TaskSwap(old,new) ; TASK *old; ; TASK *new; ; ; The current state is saved in the old structure; the current state is ; registers R2-R11, and FP. Then the current machine state is set up from ; the new structure. ; ; TaskSwap() does not return; rather, it effectively suspends within the ; current task, starting the new one. When the current process is later ; re-started (i.e., appears as new), TaskSwap() will return. ; ; TaskSwap() is NOT AST reentrant and must not be called from AST level - if ; it is, the process will remain at AST level until old is again current. .ENTRY TaskSwap,^M ; First, save the old state. R2-R11 are already saved by our entry mask. ; MOVL old(AP),R1 ;R1->old state structure MOVL FP,FPsave(R1) ;Save FP ; Now start the new state. There is a special case here: If flag bit INIT ; in the new state structure is set, this is the initial startup of the ; process. MOVL new(AP),R1 ;R1->new state structure MOVL FPsave(R1),FP ;Restore FP BBCC #TASK$V_INIT,flags(R1),10$ ;Skip if NOT initializing ; Task initialization. NewTask() initialized FPSave to point to an appropri- ; ate base frame; on startup, we must point SP to the same place. Then all ; we need do is call the initial function, and get rid of the task if it ever ; returns. MOVL FP,SP ;SP at start MOVL alist(R1),R2 ;Get arglist address MOVL func(R1),R3 ;Get function address CALLG (R2),(R3) ;Call user's code CALLS #0,TaskExit ;User returned, terminate task BPT ;Can't get here 10$: RET ;Go .END