Mixing posix threads and signal handling usually is a bit of a nightmare.
In what context is the signal handler executed?
Ruby executes the signal handler in the same thread as the parent. It can be proven by:
$stdout.sync = true
puts "parent: #{Thread.current.object_id}"
trap("TERM") { puts Thread.current.object_id }
sleep
The thread struct has interrupt_flag
and interrupt_mask
fields (dunno why they made it two fields).
When the signal is trapped, the current (main) thread is marked with TRAP_INTERRUPT_MASK
([1], [2]). While the thread is put on hold, VM runs the signal handler.
What is not safe to do from a signal handler?
I found only one place that explicitly forbids from being called inside a signal handler. This place is Mutex#lock
. It prevents user from locking any mutex from the signal handler by the design. This is not a huge limitation, but it prevents us from using Logger
which relies on Mutex
. However, puts
still works.
How do you log from the signal handler?
I've questioned myself: why can't you use Logger
inside signal trap when Resque is doing it without any troubles? The answer is that Resque is using mono_logger, which is a mutex-free logger implementation. It works just well from the signal trap!
If you're curious, here are the spots in MRI sources that define signal trap behaviour:
- thread.c#rb_threadptr_execute_interrupts
- signal.c#signal_exec
- thread.c#rb_threadptr_interrupt_common
Further reading: