Осторожность при реализации обратных вызовов Signal.trap
Как и при реализации обработчиков сигналов в C или большинстве других языков, весь код, переданный в Signal.trap, должен быть повторно вводимым. Если вы не знакомы с повторной вводимостью, вам необходимо ознакомиться с этим на Википедии или где-либо ещё, прежде чем читать остальную часть этого документа.
Самое главное, «безопасность потоков» не гарантирует повторной вводимости; и такие методы, как Mutex#lock и Mutex#synchronize, которые обычно используются для безопасности потоков, даже предотвращают повторную вводимость.
Деталь реализации Ruby VM
Ruby VM откладывает обратные вызовы Signal.trap до тех пор, пока не будет безопасно для его внутренних структур данных, но он не знает, когда безопасно для структур данных ВАШЕГО кода. Ruby реализует отложенную обработку сигналов путём регистрации коротких функций C с только функциями, безопасными при асинхронном обработке сигналов, в качестве обработчиков сигналов. Эти короткие функции C делают только необходимое для уведомления VM о выполнении обратных вызовов, зарегистрированных с помощью Signal.trap, позднее в основном потоке Ruby Thread.
Небезопасные методы для вызова в блоках Signal.trap
Если есть сомнения, считайте всё, что не указано ниже как безопасное, небезопасным.
-
Mutex#lock,Mutex#synchronizeи любой код, использующий их, явно небезопасен. Это включаетMonitorв стандартной библиотеке, которая используетMutexдля обеспечения повторной вводимости. -
Dir.chdirс блоком -
любые операции записи в
IO, когдаIO#syncложно; включаяIO#write,IO#write_nonblock,IO#puts. Трубы и сокеты по умолчанию имеют `IO#sync = true`, поэтому запись в них безопасна, если не было отключеноIO#sync. -
File#flock, так как вызов flock(2) в основе не определён POSIX
Обычно безопасные операции внутри блоков Signal.trap
-
Присваивание и получение локальных, экземпляра и класса переменных
-
Большинство выделений объектов и инициализаций общих типов, включая
Array,Hash,String,Struct,Time. -
Общие операции
Array,Hash,String,Struct, которые не выполняют блок, как правило, безопасны; но будьте осторожны, если итерация происходит где-то ещё. -
Hash#[],Hash#[]=(еслиHash.newне был передан небезопасный блок) -
Thread::Queue#push и Thread::SizedQueue#push (с Ruby 2.1)
-
Создание нового
ThreadчерезThread.new/Thread.start может помочь обойти невозможность использования Mutexes внутри обработчика сигналов -
Signal.trapбезопасно использовать внутри блоков, переданныхSignal.trap -
Арифметические операции с
IntegerиFloat(`+', `-', '%', '*', '/')Кроме того, обработчики сигналов не выполняются между двумя последовательными обращениями к локальным переменным, поэтому такие сокращения, как `+=' и `-=' не вызовут гонку данных при использовании с
IntegerиFloatклассами в обработчиках сигналов.
Методы-обертки системных вызовов, безопасные внутри Signal.trap
Поскольку Ruby имеет обертки вокруг многих функций C, безопасных при асинхронном обработке сигналов, соответствующие обертки для многих IO, File, Dir и Socket методов безопасны.
(Неполный список)
-
Dir.chdir(без аргумента блока)
…
Ruby Core © 1993–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.