Как использовать блокировку сигналов в Ruby - PullRequest
13 голосов
/ 15 июня 2011

Как блокировать определенные сигналы в Ruby? (Т.е. sigprocmask, как описано здесь: http://www.chemie.fu -berlin.de / chemnet / use / info / libc / libc_21.html # SEC371 )

Вот мой пример кода

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)

Можно ли обернуть something_that_must_not_be_interrupted в блок ruby ​​или что-то еще, чтобы гарантировать, что SIGINT не будет обрабатываться во время работы этого метода?

Полагаю, идеальным было бы, если бы я мог сделать что-то вроде этого:

Process.block_signal('INT') do
  something_that_must_not_be_interrupted
end

ОБНОВЛЕНИЕ : В настоящее время я делаю что-то вроде этого:

trap('INT') do
  @interrupted = true
end

loop do
  exit 0 if @interrupted
  something_that_must_not_be_interrupted
end

Ответы [ 2 ]

14 голосов
/ 15 июня 2011

Я думаю, вы ищете неблокированную форму trap:

Signal.trap( signal, command ) → obj
[...]
Есликоманда является строкой "IGNORE" или "SIG_IGN", сигнал будет игнорироваться.Если команда "DEFAULT" или "SIG_DFL", будет вызван обработчик Ruby по умолчанию.

Таким образом, вы можете сказать следующее:

trap('INT', 'IGNORE')
something_that_must_not_be_interrupted
trap('INT', 'DEFAULT')

ОБНОВЛЕНИЕ : Судя по комментариям, вы хотите только временно игнорировать сигнал.Самый простой способ сделать это с тем, что у вас уже есть, это добавить флаг, который может видеть ваш обработчик сигнала, затем запомнить сигнал, когда он поступит, и мы в настоящее время игнорируем сигнал, и когда мы больше не игнорируем что-то, выпросто очистите очередь сигналов, отправив их себе.Если обернуть эту логику в классе, у вас будет что-то довольно дружелюбное:

#
# 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

Реальный функциональный код был самым простым способом объяснить методику (и мне пришлось написать его в любом случае, чтобы убедиться, чтотехника будет работать) так что вы идете.И спасибо за обзор обработки сигналов в Ruby :) Тогда вы можете сделать так:

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
5 голосов
/ 03 августа 2011

Ruby возвращает последний обработчик, связанный с сигналом, поэтому вы можете использовать:

def bypass_signal(name)
  old_handler = trap("INT", 'IGNORE')
  yield
  trap("INT", old_handler)
end

bypass_signal "INT" { method_which_shouldnt_be_interrupted }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...