Previous: Implementation warts, Up: Signal handling

6.4 Programming with signal handling in mind

6.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 desctructive 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.

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

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