Previous: Implementation warts, Up: Signal handling [Contents]
Since they might be invoked in the middle of just about anything,
signal handlers must invoke only reentrant functions or async signal
safe functions to be more precise. Functions passed to
INTERRUPT-THREAD
have the same restrictions and considerations
as signal handlers.
Destructive modification, and holding mutexes to protect destructive
modifications from interfering with each other are often the cause of
non-reentrancy. Recursive locks are not likely to help, and while
WITHOUT-INTERRUPTS
is, it is considered untrendy to litter the
code with it.
Some basic functionality, such as streams and the debugger are intended to be reentrant, but not much effort has been spent on verifying it.
If functions A and B directly or indirectly lock mutexes M and N, they should do so in the same order to avoid deadlocks.
A less trivial scenario is where there is only one lock involved but
it is acquired in a WITHOUT-GCING
in thread A, and outside of
WITHOUT-GCING
in thread B. If thread A has entered
WITHOUT-GCING
but thread B has the lock when the gc hits, then
A cannot leave WITHOUT-GCING
because it is waiting for the lock
the already suspended thread B has. From this scenario one can easily
derive the rule: in a WITHOUT-GCING
form (or pseudo atomic for
that matter) never wait for another thread that’s not in
WITHOUT-GCING
.
Somewhat of a special case, it is enforced by the runtime that
SIG_STOP_FOR_GC
and SIG_RESUME_FROM_GC
always unblocked
when we might trigger a gc (i.e. on alloc or calling into Lisp).
For the reasons above, calling user code, i.e. functions passed in, or
in other words code that one cannot reason about, from non-reentrant
code (holding locks), WITHOUT-INTERRUPTS
, WITHOUT-GCING
is dangerous and best avoided.
It is not easy to debug signal problems. The best bet probably is to
enable QSHOW
and QSHOW_SIGNALS
in runtime.h and once
SBCL runs into problems attach gdb. A simple thread apply all
ba
is already tremendously useful. Another possibility is to send a
SIGABRT to SBCL to provoke landing in LDB, if it’s compiled with it
and it has not yet done so on its own.
Note, that fprintf used by QSHOW is not reentrant and at least on x86
linux it is known to cause deadlocks, so place SHOW and co carefully,
ideally to places where blockable signals are blocked. Use
QSHOW_SAFE
if you like.
Previous: Implementation warts, Up: Signal handling [Contents]