Previous: , Up: Signal handling   [Contents]


7.4 Programming with signal handling in mind

7.4.1 On reentrancy

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.

7.4.2 More deadlocks

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).

7.4.3 Calling user code

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.

7.5 Debugging

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: , Up: Signal handling   [Contents]