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 eBkChk . ePntMsg))
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
may or may not happen, depending on eBkChk and ePntMsg, which are the values of BREAKCHECK AND PRINTMESS respectively. Things from the old package
that 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)
'←])