How does one block certain signals in Ruby? (I.e. sigprocmask, as described here: http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_21.html#SEC371)
Here is my example code
pid = fork do
trap('INT') do
puts "Graceful shutdown..."
puts "goodbye"
exit 0
end
loop do
this_could_be_interrupted
something_that_must_not_be_interrupted
this_could_be_interrupted_too
end
end
sleep 5
Process.kill('INT', pid)
Can I wrap something_that_must_not_be_interrupted
in a ruby block or something to ensure that SIGINT won't be process while that method is running?
I guess the ideal would be if I could do something like this:
Process.block_signal('INT') do
something_that_must_not_be_interrupted
end
UPDATE: I'm currently doing something like this:
trap('INT') do
@interrupted = true
end
loop do
exit 0 if @interrupted
something_that_must_not_be_interrupted
end
I think you're looking for the non-block form of trap
:
Signal.trap( signal, command ) → obj
[...]
If the command is the string"IGNORE"
or"SIG_IGN"
, the signal will be ignored. If the command is"DEFAULT"
or"SIG_DFL"
, the Ruby‘s default handler will be invoked.
So you should be able to say this:
trap('INT', 'IGNORE')
something_that_must_not_be_interrupted
trap('INT', 'DEFAULT')
UPDATE: From the comments, it looks like you only want to temporarily ignore the signal. The easiest way to do that with what you already have is to add a flag that your signal handler can see, then remember the signal when it comes in and we're currently ignoring the signal, and when we're not ignoring things anymore you just empty the signal queue by sending them to yourself. If wrap this logic up in a class, you'll have something fairly friendly:
#
# Threading and race condition issues are left as an exercise,
# this is just meant as an illustration. Handling multiple signals
# at once is also left as an exercise.
#
class SignalHandler
def initialize(signal)
@interuptable = true
@enqueued = [ ]
trap(signal) do
if(@interuptable)
puts "Graceful shutdown..."
puts "goodbye"
exit 0
else
@enqueued.push(signal)
end
end
end
# If this is called with a block then the block will be run with
# the signal temporarily ignored. Without the block, we'll just set
# the flag and the caller can call `allow_interuptions` themselves.
def dont_interupt
@interuptable = false
@enqueued = [ ]
if(block_given?)
yield
allow_interuptions
end
end
def allow_interuptions
@interuptable = true
# Send the temporarily ignored signals to ourself,
# see http://ruby-doc.org/core/Process.html#method-c-kill
@enqueued.each { |signal| Process.kill(signal, 0) }
end
end
The real functional code was the easiest way to explain the technique (and I had to write it anyway to make sure the technique would work) so there you go. And thanks for the review of signal handling in Ruby :) Then you could so things like this:
sigint = SignalHandler.new('INT')
loop do
this_could_be_interrupted
sigint.dont_interupt { something_that_must_not_be_interrupted }
this_could_be_interrupted_too
end
Ruby returns the last handler associated with the signal, so you can use:
def bypass_signal(name)
old_handler = trap("INT", 'IGNORE')
yield
trap("INT", old_handler)
end
bypass_signal "INT" { method_which_shouldnt_be_interrupted }
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