Previous: Implementation warts, Up: Signal handling
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.
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
.
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.