The SIGNAL package
Henry Thompson
File:{ivy}<hthompson>lisp>rpc>signal.bravo, .press
Revised:January 22, 1983 2:13 PM
The file SIGNAL.DCOM implements a first pass at the CEDAR/MESA signal mechanism. It allows signals to be raised, caught, examined, and resumed or exited. Catch phrases are introduced with the clisp-word enable; signals are raised with the function Signal.
(Signal type arg)
Raises a signal of type type. If the signal is resumed, will return with the argument to sresume, - see below. Otherwise will not return.
(enable
s1 => a1 a2 a3 ...
. . .
sn => n1 n2 n3 ...
form
l1 -> la1 la2 ...
. . .
ln -> ln1 ln2 ...)
The double arrow lines above are called catch phrases, the single arrow lines are called finish phrases. Evaluates form so as to catch signals s1, ... sn if they are raised during its evaluation. If e.g. s1 is raised, the forms a1 ... an
(the catch phrase for s1) will be evaluated in the context of the call to Signal which raised s1, with the addition of the fact that the variables type and arg will be locally bound to type and arg. For a catch phrase to be well formed, all control paths through it must end with one of the following four quit forms:
(exit)
Causes the stack to unwind back through the enclosing enable form, which is exited with value NIL.
(sresume form)
Returns from the call to Signal with the value of form as the value of that call.
(goto label)
Causes the stack to unwind back to the enclosing enable form, where the finish phrase for label is evaluated. The value of the last form in the phrase is the value of the enable. The variables $SignalType$, $SignalArg$, and $Exit$ will be bound to type and arg of the original call to Signal and label respectively, but otherwise the environment of the call to Signal is lost.
(reject)
Causes the signal handling process to act as if the catch phrase had not been there at all.
When a signal is raised the stack is scanned upwards for a catch phrase for that signal. If none is found (or if all those found are rejected) an Uncaught Signal break will occur. Otherwise the one of the three other options listed above will occur.
There is one signal name which receives special interpretation. A catch phrase with the name any will catch any signal not explicitly caught elsewhere in the enable.
There is a special label whose name is unwind, which cannot be gone to and has a special meaning. The finish clause for the unwind label will be evaluated as the stack unwinds upwards from a call to Signal to the enable clause which caught it (or to the UserExec if ERROR! is envoked).
At the moment the package does use ERROR! and NLSETQ itself to implement the stack unwinding. This means the package interacts correctly with RESETSAVE, but has the unfortunate consequence that interleaving on the stack of calls to ERRORSET in any of its forms and enable clauses has consequences which are confusing at best.
As there are many ways in which the semantics of signals and catchers are much cleaner than those of errors and errorsets, it is hoped that at some point a more sophisticated implementation of the signal mechanism undoing the resetsaves directly and redefining ERRORSET in terms of enable will be made available.
As an interim measure however, a mechanism for turning errors into signals has been provided. The function (MakeErrorsSignals) will arrange that all errors will be converted into a signal with name LispError and argument an instance of the following record:
(RECORD LispError (eMess eFn eType . ePos))
where eMess is the standard error message pair (for printing with ERRORMESS), eFn the name of the function which the old package would have broken, eType its type, and ePos a stack pointer to its frame. If you catch this signal, you should free the stack pointer. If you don’t catch it (or catch and reject it), a break will happen, regardless of HELPDEPTH, ERRORSETs, etc. That is, BREAKCHECK is not called or paid attention to. This is the interim price of having the other benefits of the signal package, and in practice seems to work quite well. Things from the old package that do still work are ERRORTYPELST, user interrupts, and the various other funnies to do with hash arrays and EOFs which the error code handles.
To go back to the old way of life, use (MakeErrorsErrorsAgain).
There follows on the next page an toy example of the use of the package, which is in fact included in the file itself - enjoy!
(ST
[LAMBDA NIL (* ht: "20-JAN-83 21:40")
(enable
s1 => (PRINT "s1 caught" T)
(goto s1)
s2 => (PRINT "s2 caught")
(sresume 37)
s3 => (PRINT "s3 caught")
(reject)
s4 => (PRINT "s4 caught")
(exit)
any => (printout T type " caught by any" T)
(exit)
(TestSignals)
s1 -> (PRINT "s1 unwound")
unwind -> (PRINT "unwinding"))])
(TestSignals
[LAMBDA NIL (* ht: "18-JAN-83 16:39")
(printout T T
(SELECTQ (PROGN (printout T T ">")
(READ))
(1 (Signal ’s1 1))
(2 (Signal ’s2 2))
(3 (Signal ’s3 3))
(4 (Signal ’s4 4))
(5 (Signal ’foo 5))
6)
’←])