Alex Rivera | Logout

How to use signal blocking in Ruby

Asked 2011-06-15T06:45:55.127
18

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
Edit
Report

1 Answer

18

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
    #
answered 2011-06-15T08:38:19.833

Your Answer