Почему наличие разветвленного процесса в потоке изменяет поведение сна в блоке ловушек?

#ruby #multithreading #fork #child-process

Вопрос:

Это работает так, как и ожидалось:

 signals = %w[INT TERM]
signals.each do |signal|
  Signal.trap(signal) do
    puts "trapping the signal and sleeping for 5 seconds"
    sleep 5
    puts "done, exiting"
    exit
  end
end

sleep 100
 

так же как и это:

 # ...signal trap code from above...

t = Thread.new{ sleep 10 }
t.join
 

так же как и это:

 # ...signal trap code from above...

`sleep 10`
 

однако это не означает:

 # ...signal trap code from above...

t = Thread.new{ `sleep 10` }
t.join
 

Для первых трех запуск кода, а затем немедленная отправка control-c результатов в ruby, ожидание 5 секунд перед выходом.

Для четвертого, запуск кода, а затем немедленная отправка control-c результатов в ruby, немедленно завершается. Что удивительно, так это то, что два puts сообщения, «захват…» и «готово…», оба напечатаны, но sleep 5 промежуточное между ними, по-видимому, пропущено.

использование terrapin вместо обратных ссылок дает еще одну подсказку — terrapin жалуется, что дочерние процессы вышли из ненулевого состояния. при попытке распечатать этот статус он ничего не печатает, возможно, предполагая, что процесс был бесцеремонно жестко прерван.

Таким образом, похоже, что у ruby есть какое-то поведение по умолчанию в отношении дочерних процессов, созданных в потоках, которые не являются основным потоком. Я подозревал, что это может быть сделано специально, но я не смог найти никакой документации или обсуждения по этому поводу.

Я тоже пытался

 t = Thread.new{ `ls -R /` }
 

вместо сна, на всякий случай, было какое-то взаимодействие с конкурирующими реализациями сна, такое же поведение.

Почему это происходит?

Больше Экспериментов

Это также ведет себя так, как и ожидалось. он ожидает завершения подпроцесса в потоке 10 секунд. таким образом, странное поведение происходит только в контексте сигнальной ловушки.

 thread = Thread.new { `sleep 10` }
thread.join
 

Чтобы проверить, происходит ли что-то, связанное с одновременным захватом сигнала подпотоком, либо по замыслу, либо из-за ошибки. Но это ведет себя так, как и ожидалось:

 @main_thread = Thread.current.object_id
puts "main thread: #{@main_thread}"

signals = %w[INT TERM]
signals.each do |signal|
  Signal.trap(signal) do
    puts Thread.current.object_id
    next unless Thread.current.object_id == @main_thread
    puts "thread from trap: #{Thread.current.object_id}"
    puts "trapping the signal and sleeping for 5 seconds"
    sleep 5
    puts "done, exiting"
    exit
  end
end
 

Комментарии:

1. Вы устанавливаете ловушку в своем основном процессе, а не в каждом потоке, поэтому я ожидал бы такого поведения как неподготовленный SIGTERM.

2. afaik, ловушки предназначены для каждого процесса, так что это не должно иметь значения… но да, мне интересно, связано ли что-то в этом роде