I'm trying to catch a signal using the Kernel#trap, and run a database request in that context, but I'm getting the above error. Has anyone come across that before? Is there any way around it?
Sample code:
trap('HUP') do
$db[:db_name].update({_id: 123}, {:$set => {a: 1}})
end
loop { sleep 1 }
Will give this error:
/usr/local/lib/ruby/gems/2.1.0/gems/mongo-1.11.1/lib/mongo/connection/pool.rb:266:in `synchronize': can't be called from trap context (ThreadError)
when the script receives the HUP signal, which can be sent by running kill -HUP {pid}. $db must be a MongoDB object.
Ruby doesn't allow Mutex synchronization from within a trap context, presumably since it can result in deadlocks (ie, you're in a synchronized context, then send the process a signal and attempt to resynchronize, upon which you deadlock). You can reproduce this trivially via:
# trap.rb
require 'thread'
mutex = Mutex.new
trap('HUP') { mutex.synchronize {} }
gets
# pkill -HUP -f trap.rb
trap.rb:3:in `synchronize': can't be called from trap context (ThreadError)
from trap.rb:3:in `block in <main>'
from trap.rb:4:in `call'
from trap.rb:4:in `gets'
from trap.rb:4:in `gets'
from trap.rb:4:in `<main>'
To solve this, your signal handler should probably queue a job to be handled by another thread, or you can just spawn a new thread and perform your work in there:
# trap.rb
require 'thread'
mutex = Mutex.new
trap('HUP') do
Thread.new { mutex.synchronize { puts "hi!" } }
end
gets
# pkill -HUP -f trap.rb
hi!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With