SignalsImpl.mesa
Copyright Ó 1987, 1989, 1991 by Xerox Corporation. All rights reserved.
Carl Hauser, July 24, 1989 12:51:53 pm PDT
Russ Atkinson (RRA) July 27, 1989 9:05:46 pm PDT
Christian Jacobi, January 5, 1990 5:24:28 pm PST
Chauser, February 22, 1991 10:51 am PST
Foote, May 31, 1991 2:44 pm PDT
Willie-s, August 5, 1991 5:34 pm PDT
Michael Plass, December 13, 1991 3:22 pm PST
Global Variables (protected by monitor)
ucsHandler: RuntimeError.UCSProc ¬ InitialUCSHandler;
SigInfo:
TYPE ~
RECORD [
sig: ExceptAny,
arg: POINTER,
rtns: POINTER,
informational: BOOL
];
InformationalSignal:
PUBLIC
SAFE
PROC [signal:
SIGNAL] =
TRUSTED {
Who makes informational signals? What language feature? According to Dan Swinehart, this kind of signalling is done in code written by wizards and not produced by any Mesa language feature. The idea is to signal in such a way that if the signal is not caught, you don't end up in the debugger.
SignalHandler[signal, NIL, NIL, TRUE];
};
RegisterUncaughtSignalHandler:
PUBLIC
-- ENTRY --
PROC [proc: RuntimeError.UCSProc]
RETURNS [old: RuntimeError.UCSProc] = {
old ¬ ucsHandler;
ucsHandler ¬ proc;
};
InitialUCSHandler: RuntimeError.UCSProc ~ {
CallDebugger[];
};
CatchProc: TYPE ~ PCRS.Handler;
HandlerAction:
TYPE =
PCRS.HandlerAction;
NoHandler: CatchPhrase = NIL;
SignalHandler
signalSpy: PROC ¬ NIL;
RegisterSignalSpy:
PUBLIC
-- ENTRY --
PROC [new:
PROC]
RETURNS [old:
PROC] ~ {
old ¬ signalSpy;
signalSpy ¬ new;
};
SignalHandler:
PROC
[sig: ExceptAny, arg:
POINTER, rtns:
POINTER, informational:
BOOL ¬
FALSE] ~
TRUSTED {
action: PCRS.HandlerAction;
catchProc: PCRS.Handler;
warmestHandler: CatchPhrase ¬ NIL;
selfObj: SignallerStateObject ¬ [];
self: SignallerState ¬ LOOPHOLE[@selfObj];
exitTo: INT;
spy: PROC ~ signalSpy;
IF spy # NIL THEN spy[];
self.signal ¬ sig;
TRUSTED {
OPEN self;
unwinding ¬ FALSE;
warmestHandler ¬ WarmestHandler[]; -- in the current thread
First pass: find catch phrases, looking for GOTO out of one
unwinding = FALSE
start ¬ warmestHandler;
nextCatchPhrase ¬ start;
UNTIL (nextCatchPhrase = target)
OR unwinding
DO
thisSignaller: SignallerState;
walk from nextFrame toward "cool" end of stack, looking for target
currentCatchPhrase ¬ nextCatchPhrase;
thisSignaller ¬ currentCatchPhrase.signallerState;
IF thisSignaller #
NIL
THEN {
catchPhrase is for another instance of this (SignalHandler) procedure.
choose our nextFrame
IF signal ~= thisSignaller.signal
THEN {
nextCatchPhrase ¬ Parent[currentCatchPhrase];
}
ELSE {
nextCatchPhrase ¬ thisSignaller.nextCatchPhrase;
};
different signals propagate through intervening frames
from the Mesa 11.0 manual: "all of the routines whose frames lie beyond thisSignaller, up to the frame containing the catch phrase called by thisSignaller, have already had a chance to handle signal, so they are not given it again."
I claim that all signals should propagate through intervening frames, or none. I lean to the side of none, but I'll leave this as-is for the time being.
}
ELSE
-- currentCatchPhrase.signallerState = NIL --
frame is NOT for another SignalHandler - just walk the stack to nextFrame
{
nextCatchPhrase ¬ Parent[currentCatchPhrase];
};
Will this frame catch any signals? It may, depending on the body of the catch phrase (it may also raise signals!)
IF (catchProc ¬ currentCatchPhrase.catchProc) #
NIL
THEN {
call the catch phrase
ENABLE {
RuntimeError.SendMsg => RESUME[self.signal, arg, rtns]; -- GetSignal is SIGNALled by an ANY-catcher to request the details of the signal it is processing --
};
now WarmestHandler will give us the catchPhrase just pushed for the above ENABLE
hotHandler: CatchPhrase ¬ WarmestHandler[];
stuff our "self" record in the catchPhrase so later instances of the SignalHandler can find it.
hotHandler.signallerState ¬ self;
[action, exitTo] ¬
catchProc[context: currentCatchPhrase.catchContext, except: signal, rtnPtr: rtns, argPtr: arg ! RuntimeError.Resume => TRUSTED { action ¬ resume; CONTINUE }];
SELECT action
FROM
reject => NULL;
resume => {
the handler has filled rtns^ with the results for signal's raiser
exit =>
IF ~informational
THEN {
target ¬ currentCatchPhrase;
unwinding ¬ TRUE;
arg ¬ NIL;
};
ELSE
Apparently, non-local GOTOs from a handlers for signals originated as informational are ignored and treated as REJECTs.
ENDCASE;
};
ENDLOOP;
What happened?
SELECT
TRUE
FROM
unwinding => NULL;
informational
OR signal =
LOOPHOLE[
UNCAUGHT] =>
NULL;
does an implicit RESUME
Informational signals are resumed either by an explicit resume in a catch phrase or when the signal has propagated to the top of the stack. IF signal = LOOPHOLE[UNCAUGHT] at this point, it was raised in the following ENDCASE (in a different invocation of SignalHandler), so upon resumption, that SignalHandler calls ucsHandler.
ENDCASE => {
We want to be able to RESUME UNCAUGHT, but we don't want a random catch phrase to do so. So, we declare it to be an ERROR in the interface, but raise it as a SIGNAL here. When (if) UNCAUGHT percolates to the top of the stack, it is implicitly resumed (see the preceding line of code). Control then passes to the statement following the invocation of UNCAUGHT, which calls the debugger (or whatever).
Uncaught: TYPE = SIGNAL [SIGNAL ANY RETURNS ANY, WORD];
LOOPHOLE[UNCAUGHT, Uncaught][signal, LOOPHOLE[arg]];
for now dummy msg parameter because the PrincOps world is so different.
frame: NIL because I think that's what happens in PrincOps world.
TRUSTED {ucsHandler[msg: LOOPHOLE[arg], signal: signal, frame: NIL]};
};
IF unwinding
THEN {
disable ordinary handlers in the stack to be unwound
nextCatchPhrase ← start;
UNTIL (nextCatchPhrase = target) DO
nextCatchPhrase.dynamicFrame.key ← DisabledMesaHandlerKey;
nextCatchPhrase ← Parent[nextCatchPhrase];
ENDLOOP;
target.dynamicFrame.key ← DisabledMesaHandlerKey;
propagate an UNWIND
UnwindTo[LOOPHOLE[target]];
RestartHandlee[GetNextHandlee[NIL], target.jmpBufPtr, exitTo]
};
};
};
Parent:
PROC [catchPhrase: CatchPhrase]
RETURNS [parent: CatchPhrase]
~
TRUSTED {
follows the caller pointer of frame
env: DynamicFrame ~ LOOPHOLE[catchPhrase];
RETURN[LOOPHOLE[LookupInDynamicEnvironment[MesaHandlerKey, env.next]]];
};
WarmestHandler:
PROC []
RETURNS [CatchPhrase] ~ {
env: DynamicFrame ~ GetDynamicEnvironment[];
RETURN[LOOPHOLE[LookupInDynamicEnvironment[MesaHandlerKey, env]]];
};
MesaUnwinderProc: TYPE ~ POINTER TO MesaUnwinderProcBody;
MesaUnwinderProcBody:
TYPE ~
RECORD [
codeBody: WORD,
catchPhrase: CatchPhrase
];
MesaUnwinder:
PROC [self: MesaUnwinderProc] ~
TRUSTED {
-- XR¬
MesaUnwinder
cp: CatchPhrase ¬ self.catchPhrase;
catchProc: PCRS.Handler;
action: PCRS.HandlerAction;
IF (catchProc ¬ cp.catchProc) #
NIL
THEN
TRUSTED {
ENABLE {
RuntimeError.SendMsg => RESUME[LOOPHOLE[UNWIND], NIL, NIL]; -- GetSignal is SIGNALled by an ANY-catcher to request the details of the signal it is processing --
};
call the catch phrase -- this is old-style, with UNWIND handling buried in normal catch phrases.
[action, ] ¬
catchProc[context: cp.catchContext, except: LOOPHOLE[UNWIND], rtnPtr: NIL, argPtr: NIL ! RuntimeError.Resume => {action ¬ resume; CONTINUE }];
SELECT action
FROM
reject => NULL;
resume => ERROR RuntimeError.ResumeFault;
exit => ERROR RuntimeError.UnwindFault;
ENDCASE;
};
};
Signalling types
Maintain correspondence with declaration in DefSetSignalEnvironment below
SignalEnvironment: TYPE = REF SignalEnvironmentObject;
SignalEnvironmentObject:
TYPE =
MACHINE
DEPENDENT
RECORD [
unixTrapHandler: PROC,
aborted: ERROR,
abstractionFault: ERROR,
arithmeticFault: ERROR,
assignRefCompositeFault: ERROR,
boundsFault: ERROR,
divideCheck: SAFE SIGNAL,
invalidProcess: ERROR [process: PROCESS],
linkageFault: ERROR,
narrowFault: ERROR,
narrowRefFault: ERROR [ref: REF, type: RuntimeError.Type],
nestedProcFault: ERROR [proc: RuntimeError.ProcAny],
nilFault: ERROR,
resumeFault: ERROR,
sendMsg: SIGNAL RETURNS [signal: SIGNAL ANY RETURNS ANY, args, results: POINTER],
stackFault: ERROR,
startFault: SIGNAL [dest: PROGRAM],
unboundProcedureFault: SIGNAL [dest: PROC ANY RETURNS ANY] RETURNS [PROC ANY RETURNS ANY],
uncaught: ERROR [signal: SIGNAL ANY RETURNS ANY, parameters: WORD],
unnamedError: ERROR,
unnamedSignal: SIGNAL,
unwind: ERROR,
unwindFault: ERROR,
zeroDivisor: SAFE SIGNAL
];
formerly from SignalSupport.c and SignalSupport.h:
DefSetSignalEnvironment:
PROC [] =
TRUSTED
MACHINE
CODE {
"+";
"typedef struct {\n";
" int *unixTrapHandler;\n";
" unsigned aborted;\n";
" unsigned abstractionFault;\n";
" unsigned arithmeticFault;\n";
" unsigned assignRefCompositeFault;\n";
" unsigned boundsFault;\n";
" unsigned divideCheck;\n";
" unsigned invalidProcess;\n";
" unsigned linkageFault;\n";
" unsigned narrowFault;\n";
" unsigned narrowRefFault;\n";
" unsigned nestedProcFault;\n";
" unsigned nilFault;\n";
" unsigned resumeFault;\n";
" unsigned sendMsg;\n";
" unsigned stackFault;\n";
" unsigned startFault;\n";
" unsigned unboundProcedureFault;\n";
" unsigned uncaught;\n";
" unsigned unnamedError;\n";
" unsigned unnamedSignal;\n";
" unsigned Unwind;\n";
" unsigned unwindFault;\n";
" unsigned zeroDivisor;\n";
" } SignalEnvironmentObject;\n";
"typedef SignalEnvironmentObject *SignalEnvironment;\n";
"unsigned XR←UnnamedSignal;\n";
"unsigned XR←UnnamedError;\n";
"unsigned XR←Unwind;\n";
"unsigned XRorted;\n";
"unsigned XR←Uncaught;\n";
"unsigned XR←NarrowFault;\n";
"unsigned XR←NarrowRefFault;\n";
"unsigned XR←InvalidProcess;\n";
"SignalEnvironment XR←SigEnv;\n";
"static void XR←SetSignalEnvironment(env)\n";
" SignalEnvironment env;\n";
" {\n";
" XR←SigEnv = env;\n";
" XR←UnnamedError = env-> unnamedError;\n";
" XR←UnnamedSignal = env->unnamedSignal;\n";
" XR←Unwind = env->Unwind;\n";
" XRorted = env->aborted;\n";
" XR←Uncaught = env->uncaught;\n";
" XR←NarrowFault = env->narrowFault;\n";
" XR←NarrowRefFault = env->narrowRefFault;\n";
" XR←InvalidProcess = env->invalidProcess;\n";
" };\n";
".";
};
ExceptAny: TYPE ~ SIGNAL ANY RETURNS ANY;
MesaHandlerKey: WORD ~ CODE;
DisabledMesaHandlerKey: WORD ~ CODE;
UnwindFrameKey: WORD ¬ GetUnwindFrameKey[];
DynamicFrame: TYPE ~ POINTER TO DynamicFrameObject;
DynamicFrameObject:
TYPE ~
RECORD [
next: DynamicFrame,
key: WORD,
data: WORD
];
UnwindFrame: TYPE ~ POINTER TO UnwindFrameObject;
UnwindFrameObject:
TYPE ~
RECORD [
next: DynamicFrame,
key: WORD,
unwinder: PROC,
rewinder: PROC
];
assertUnwindFrameObjectSize: [0..4] =
WORDS[UnwindFrameObject];
If this fails to compile, change the number in "unwindSpace[4]" in XR𡤎nable
CatchPhrase: TYPE ~ POINTER TO CatchPhraseObject;
CatchPhraseObject:
TYPE ~
RECORD [
dynamicFrame: DynamicFrameObject,
jmpBufPtr: POINTER,
catchProc: PCRS.Handler,
catchContext: POINTER,
signallerState: REF SignallerStateObject
];
assertCatchPhraseObjectSize: [0..7] =
WORDS[CatchPhraseObject];
If this fails to compile, change the number in "catchSpace[7]" in XR𡤎nable
SignallerState: TYPE = REF SignallerStateObject;
SignallerStateObject:
TYPE ~
RECORD [
startMark: CARD32 ¬ 0FF00FF00H,
start: CatchPhrase ¬ noCP,
signal: ExceptAny ¬ NIL,
currentCatchPhrase: CatchPhrase ¬ noCP,
nextCatchPhrase: CatchPhrase ¬ noCP,
target, nextTarget: CatchPhrase ¬ noCP,
unwinding: BOOL ¬ FALSE,
endMark: CARD32 ¬ 0FF11FF11H
];
RaiseErrorProc:
TYPE =
PROC [which: ExceptAny, args:
POINTER];
RaiseSignalProc: TYPE = PROC [which: ExceptAny, rtns: POINTER, args: POINTER];
XR routines
CallDebugger:
PROC []
RETURNS [] ~
TRUSTED
MACHINE
CODE {
"XRllDebugger"
};
SetSignalEnvironment:
PROC [env: SignalEnvironment] ~
TRUSTED
MACHINE
CODE {
"XR←SetSignalEnvironment"
};
RaiseError:
PUBLIC RaiseErrorProc =
TRUSTED {
-- XR¬
RaiseError
stats.errorsRaised ¬ stats.errorsRaised + 1;
SignalHandler[which, args, NIL];
ERROR RuntimeError.ResumeFault
};
RaiseSignal:
PUBLIC RaiseSignalProc =
TRUSTED {
-- XR¬
RaiseSignal
stats.signalsRaised ¬ stats.signalsRaised + 1;
SignalHandler[which, args, rtns];
};
RaiseUnnamedError:
PROC []
RETURNS [
CARD ¬ 0] ~ {
-- XR¬
RaiseUnnamedError
--make this procedure PUBLIC when interface changes for any other reason
RaiseError[LOOPHOLE[RuntimeError.UnnamedError], NIL]
};
RaiseArithmeticFault:
PUBLIC
PROC []
RETURNS [
CARD ¬ 0] ~ {
-- XR¬
RaiseArithmeticFault
RaiseError[LOOPHOLE[RuntimeError.ArithmeticFault], NIL]
};
RaiseBoundsFault:
PUBLIC
PROC []
RETURNS [
CARD ¬ 0] ~ {
-- XR¬
RaiseBoundsFault
RaiseError[LOOPHOLE[RuntimeError.BoundsFault], NIL]
};
RaiseAbstractionFault:
PUBLIC
PROC []
RETURNS [] ~ {
-- XR¬
RaiseAbstractionFault
RaiseError[LOOPHOLE[RuntimeError.AbstractionFault], NIL]
};
SetupPushHandler:
PROC
[context:
POINTER, proc:
PCRS.Handler, catchPhrase: CatchPhrase, unwindFrame: UnwindFrame, unwinder:
PROC, jmpBufPtr:
POINTER ]
~
TRUSTED {
-- XR¬
SetupPushHandler
prevEnv: DynamicFrame ¬ GetDynamicEnvironment[];
catchPhrase.dynamicFrame.next ¬ LOOPHOLE[unwindFrame];
catchPhrase.dynamicFrame.key ¬ MesaHandlerKey;
catchPhrase.catchProc ¬ proc;
catchPhrase.catchContext ¬ context;
catchPhrase.signallerState ¬ NIL;
catchPhrase.jmpBufPtr ¬ jmpBufPtr;
unwindFrame.next ¬ prevEnv;
unwindFrame.key ¬ UnwindFrameKey;
unwindFrame.unwinder ¬ unwinder;
unwindFrame.rewinder ¬ NIL;
SetDynamicEnvironment[LOOPHOLE[catchPhrase]];
stats.newPushCount ¬ stats.newPushCount+1;
};
PushAllocedHandler:
PUBLIC
PROC
[context:
POINTER, handler:
PCRS.Handler, jmpBuf:
POINTER]
RETURNS [
INT]
~
TRUSTED {
-- XR¬
PushAllocedHandler
the jmpBuf^ should be dynamically allocated (not on the stack)
unwindFrame: UnwindFrame ¬ LOOPHOLE[NEW[UnwindFrameObject]];
catchPhrase: CatchPhrase ¬ LOOPHOLE[NEW[CatchPhraseObject]];
MesaProcObject:
TYPE ~
RECORD [
codePointer: WORD,
context: WORD
];
MesaProc: TYPE ~ POINTER TO MesaProcObject;
prevEnv: DynamicFrame ¬ GetDynamicEnvironment[];
catchPhrase.dynamicFrame.next ¬ LOOPHOLE[unwindFrame];
catchPhrase.dynamicFrame.key ¬ MesaHandlerKey;
catchPhrase.catchProc ¬ handler;
catchPhrase.catchContext ¬ context;
catchPhrase.signallerState ¬ NIL;
catchPhrase.jmpBufPtr ¬ jmpBuf;
unwindFrame.rewinder ¬ NIL;
unwindFrame.next ¬ prevEnv;
unwindFrame.key ¬ UnwindFrameKey;
unwindFrame.unwinder ¬ LOOPHOLE[NEW[MesaProcObject ¬ [LOOPHOLE[MesaUnwinder, MesaProc].codePointer, LOOPHOLE[catchPhrase]]]];
SetDynamicEnvironment[LOOPHOLE[catchPhrase]];
stats.pushCount ¬ stats.pushCount+1;
RETURN[0];
dead, but it keeps the compiler happy
};
PopHandler:
PUBLIC
PROC []
RETURNS [] ~
TRUSTED {
-- XR¬
PopHandler
env: DynamicFrame ¬ GetDynamicEnvironment[];
IF (env.key = MesaHandlerKey
OR env.key = DisabledMesaHandlerKey)
AND env.next.key=UnwindFrameKey
THEN {
SetDynamicEnvironment[env.next.next]
}
ELSE ERROR;
};
Embedded C procedure (for XR𡤎nable)
DefEnable:
PROC [] =
TRUSTED
MACHINE
CODE {
"+";
"#include <xr/Threads.h>\n";
"typedef struct {fPt p; word c;} ProcDescBody;\n";
"extern word XR𡤎nable(body, handler, context)\n";
" fPt body;\n";
" fPt handler;\n";
" ptr context;\n";
" {\n";
" word rtnCode;\n";
" ptr beforeCall;\n";
" ProcDescBody pdDescBody;\n";
" ProcDescBody unwindDescBody;\n";
" word catchSpace[7];\n";
" struct XR←JmpBufRep jmpBuf;\n";
" word unwindSpace[4];\n";
" pdDescBody.p = handler;\n";
" pdDescBody.c = 1;\n";
" unwindDescBody.p = (fPt) XR←MesaUnwinder;\n";
n.b. the context for the unwinder is the catchPhrase, not the unwindFrame
" unwindDescBody.c = (word) catchSpace;\n";
" beforeCall = (ptr) XR←GetDynamicEnvironment();\n";
" XR←SetupPushHandler(context, &pdDescBody, catchSpace, unwindSpace, &unwindDescBody, &jmpBuf);\n";
" rtnCode = XR←setjmp(&jmpBuf);\n";
" if (rtnCode == 0) rtnCode = body(context);\n";
" { /* INLINE version of XR←PopHandler, no stats */ \n";
" ptr curr = (ptr) XR←GetDynamicEnvironment();\n";
" ptr prev = (ptr) *((ptr) *curr);\n";
" if (beforeCall != prev) XR←RaiseError(0,0);\n";
" XR←SetDynamicEnvironment(prev);\n";
" };\n";
" return (rtnCode);\n";
" };\n";
"extern word XR←UnwindFrameKey;\n";
"static word XR←GetUnwindFrameKey()\n";
" {\n";
" return( XR←UnwindFrameKey );\n";
" };\n";
".";
};