Ok, I think I know how to make SBCL signal safe for the most part.
Tenative documentation and very sketchy imp notes follows.
Does this look acceptable? I think except for the POSIX SIGNALS part
this jives pretty well with what Windows will require at any rate, but
I've very little idea yet of how this works with the Mach kernel
thread thingamajik (but if it can deal with our current approach, it
should certainly be able to deal with this!)
I went thought two different approaches yesterday / last night, and
discovered that they either needed signal unsafe functions in handlers,
or were prohibitively expensive. This should be free from those malaises,
but the actual implementation is vaporvare rigth now, so who knowns for
The way I see it, the biggest difference is that we (by default) we
lose the lovely asynch timers Xach gave us on unithreaded builds.
Mostly due to that this more or less needs to tie in with the safe
timeout system I mentioned earlier.
Interrupts are unexpected events that happen due to things beoynd the
program's control: user sending a keyboard interrupt, for example.
There are major classes of interrupts: synchronous and asynchronous.
Synchronous interrupts are deferred till the next call to
RECEIVE-INTERRUPTS that happens outside a WITHOUT-INTERRUPTS section.
Synchronous interrupts are always safe, and it is safe to unwind from
a synchronous interrupt.
There are many sources of synchronous interrupts in the system: On
single-threaded builds timer events are syncronous by default, but can
be made asyncronous on POSIX systems using the function
TIMER-INTERRUPTS. (On multithreaded builds timer events are handled in
a separate thread.)
Most POSIX signals are by default handled as synchrous interrupts, but
this can be altered using SIGNAL-ACTION.
Does nothing if called during the execution of a
WITHOUT-INTERRUPTS, unless there is an intervening
Otherwise, if there are any deferred interrupt, handles at most
one of each kind and then returns, unless a handler performed a
Since interrupt handlers may execute arbitrary code,
RECEIVE-INTERRUPTS may signal any kind of condition, execute a
non-local exit, or even terminate the process.
SBCL never calls RECEIVE-INTERRUPTS due to a call to another
function, and does not automatically insert calls to it to user
code. The only places where SBCL automatically calls
RECEIVE-INTERRUPTS is between iterations of various interactive
User functions that call RECEIVE-INTERRUPTS should advertise
this in their documentation, so that their callers know to
expect the unexptected.
function TIMER-INTERRUPTS &optional style
STYLE must be either NIL, :ASYNCHRONOUS, :SYNCHRONOUS, or :THREAD.
If no STYLE is given or it is NIL, the current timer interrupt
style is returned. Otherwise any future timer event are handled in
using the specified type of interrupt.
:ASYNCHRONOUS style is supported only on POSIX systems, and only
for backwards compatibility. Using it is never fully safe, and
may lead to undefined consequences. Using it allows timers to
fire using asynchronous interrupts.
:SYNCHRONOUS style is the default on single-threaded builds, using
it is safe and limits timers to firing when RECEIVE-INTERRUPTS is
:THREAD style is the default on multithreaded build, using it is
safe, and limits timers to firing within a specific timer thread.
macro WITHOUT-INTERRUPTS &body forms
Executes FORMS with all interrupts disabled, unless a nested
Does not interfere with garbage collection, and does not prevent
scheduling of other threads on multithreaded builds.
macro WITH-INTERRUPTS &body forms
Executes FORMS with all interrupts enabled. Since interrupts are
normally enabled, this does not have any effect unless an outer
Asynchronous interrupts are handled as soon as possible: they are
deferred while inside a WITHOUT-INTERRUPTS section, but are handled as
soon as it is left, or if a WITH-INTERRUPTS section is entered. Most
of the time they are handled essentially immediately.
IMPORTANT: Asyncronous interrupts are never quite safe, and unwinding
from them is especially risky: they may arrive eg. in the middle of a
non-reentrant system call, or between a function call and assigning
its result to a variable.
Asynchronous interrupts can still be useful for debugging, as they are
able to eg. interrupt endless loops that contain no RECEIVE-INTERRUPTS
calls. They should, however, generally be avoided in production
systems, unless hard to diagnose crashes or hangs are acceptable.
By default the following are the only sources of asyncronous
interrupts in SBCL:
* INTERRUPT-THREAD and TERMINATE-THREAD are always asynchronous. Their
synchronous equivalents are called CALL-IN-THREAD and UNWIND-THREAD.
* The system interactive interrupt (SIGINT on POSIX systems) is by
default asynchronous, but can be made synchronous by using
* The statistical profiler works currently by using asyncronous
interrupts, and can therefore cause occasional erratic behaviour.
function INTERACTIVE-INTERRUPTS &optional style
STYLE must be either NIL, :ASYNCHRONOUS, or :SYNCHRONOUS.
If no STYLE is given or it is NIL, the current interactive
interrupt style is returned. Otherwise any future interactive
interrupts are handled in using the specified type of interrupt.
:ASYNCHRONOUS is the default, but is recommended only for
development and debugging, as it is never quite safe, and
unwinding from such an interactive interrupt is especially risky.
It is the default style for both backwards compatibility and to
allow interruption of eg. endless loops.
:SYNCHRONOUS style recommended for production environments, and
defers interactive interrupts till the next call to
There are two major classes of signals from the SBCL perspective:
system signals, and user signals. System signals have a predefined
behaviour can cannot be altered by user code without breaking the
system. User signals can have a range of behaviours settable via
function SIGNAL-ACTION signal &optional action argument
Allows defining an action to be taken for handling a user signal.
Signal must be one of the following keywords (matching the
corresponding POSIX signal): ...list of "user signals" here...
Returns as multiple values the previous ACTION and ARGUMENT.
Possible actions and their arguments are:
NIL no argument Retains current behaviour.
:ignore no argument Ignores the signal.
:terminate message Terminates the lisp process.
:stop message Stops the lisp process.
:synchronous handler Defers the handler to next safe point.
:asynchronous handler Handler is called immediately.
:thread handler Handler is called in separate thread.
function DEFAULT-SIGNAL-ACTION signal
Causes the system to take the default action for the specified
SIGNAL number. Calling this function is the correct way to
terminate or stop the process in response to a signal whose
default action is to terminate or stop the process after a handler
has first run: it ensures that the controlling shell is informed
about the signal that caused process termination.
Note that if the default POSIX action is to ignore the signal,
then this function does nothing.
When deferring handling of a signal, we set a global flag, use a
semaphore to lock the pending signal array, mark the specific signal
as pending in the array, and up the semaphore. Multiple pending
signals are lost and we don't care: two SIGINTs is the same as one for
RECEIVE-INTERRUPTS checks the global flag, locks the signal array,
finds the first pending signal from it, zeroes it, releases the
lock, and calls the deferred handler. It repeats this for all for all
pending signals, but at most once for any given signal. This process
is guaranteed to terminate, and can safely be unwound from.
SIGCHLD needs to be thought about some more. We may eg. want to
save the si_code.
SIGPROF, SIGVTALARM: handle immediately for now (unsafe, but we want
these for profiling). someday maybe save context and a snapshot of
stack for analysis at a safe point before next GC. These will still
need to respect WITHOUT-INTERRUPTS, though, to give some measure of
safety. (But maybe we could actually make them bypass that, and have
a separate WITHOUT-INTERNAL-INTERRUPTS that defers them too?)
SIGPIPE, SIGIO/SIGPOLL: ?
SIGBUS, SIGFPE, SIGILL, SIGSEGV, and SIGTRAP are deal with pretty
much as now: they are either something we arranged to happen, or
something potentially bad enough that all bets are off at any rate.