This works as expected:
signals = %w[INT TERM]
signals.each do |signal|
Signal.trap(signal) do
puts "trapping the signal and sleeping for 5 seconds"
sleep 5
puts "done, exiting"
exit
end
end
sleep 100
so does this:
# ...signal trap code from above...
t = Thread.new{ sleep 10 }
t.join
so does this:
# ...signal trap code from above...
`sleep 10`
however, this does not:
# ...signal trap code from above...
t = Thread.new{ `sleep 10` }
t.join
For the first three, starting the code and then immediately sending control-c results in ruby waiting 5 seconds before exiting.
For the fourth, starting the code and then immediately sending control-c results in ruby immediately exiting. What's amazing is that the two puts messages, "trapping the..." and "done...", are both printed, but the sleep 5 in between them is seemingly skipped over.
using terrapin instead of backticks produces another clue - terrapin complains that the child processes exited with non-zero state. when attempting to print this status, it prints nothing, perhaps suggesting that the process was unceremoniously hard killed.
So it seems that ruby has some sort of default behavior regarding child processes created within threads that are not the main thread. I suspected this might be by design but I wasn't able to find any documentation or discussion about it.
I also tried
t = Thread.new{ `ls -R /` }
instead of sleep, just in case there was some sort of interaction with competing sleep implementations, same behavior.
Why is this happening?
More Experiments
This also behaves as expected. it waits 10 seconds for the subprocess within the thread to finish. so, the strange behavior only happens in the context of a signal trap.
thread = Thread.new { `sleep 10` }
thread.join
To test if something is happening involving a subthread trapping the signal at the same time, either by design or from a bug. But this behaves as expected:
@main_thread = Thread.current.object_id
puts "main thread: #{@main_thread}"
signals = %w[INT TERM]
signals.each do |signal|
Signal.trap(signal) do
puts Thread.current.object_id
next unless Thread.current.object_id == @main_thread
puts "thread from trap: #{Thread.current.object_id}"
puts "trapping the signal and sleeping for 5 seconds"
sleep 5
puts "done, exiting"
exit
end
end