Подтвердить что ты не робот

Как использовать блокировку сигналов в Ruby

Как блокировать определенные сигналы в Ruby? (I.e. 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

UPDATE. Я сейчас делаю что-то вроде этого:

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

loop do
  exit 0 if @interrupted
  something_that_must_not_be_interrupted
end
4b9b3361

Ответ 1

Я думаю, что вы ищете неблокированную форму 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://www.ruby-doc.org/core/classes/Process.html#M001286
        @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

Ответ 2

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

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

bypass_signal "INT" { method_which_shouldnt_be_interrupted }