Предостережения при реализации callback-функций Signal.trap
Как и при реализации обработчиков сигналов на C или большинстве других языков, весь код, переданный в Signal.trap, должен быть реентерабельным. Если вы не знакомы с реентерабельностью, прежде чем читать остальную часть этого документа, ознакомьтесь с этой темой в Википедии или другом источнике.
Самое важное: «потокобезопасность» не гарантирует реентерабельность; а такие методы, как Mutex#lock и Mutex#synchronize, которые обычно используются для обеспечения потокобезопасности, даже препятствуют реентерабельности.
Особенность реализации виртуальной машины Ruby
Виртуальная машина Ruby откладывает выполнение callback-функций Signal.trap, пока это не станет безопасным для её внутренних структур данных, но она не знает, когда это безопасно для структур данных ВАШЕГО кода. Ruby реализует отложенную обработку сигналов, регистрируя короткие функции на C, в которых в качестве обработчиков сигналов используются только асинхронно-безопасные функции. Эти короткие функции на C лишь сообщают виртуальной машине, что нужно позднее выполнить callback-функции, зарегистрированные через Signal.trap, в главном потоке Ruby Thread.
Небезопасные методы для вызова в блоках Signal.trap
Если вы сомневаетесь, считайте небезопасным всё, что не указано ниже как безопасное.
-
Mutex#lock, Mutex#synchronize и любой код, использующий их, явно небезопасны. Сюда входит
Monitorиз стандартной библиотеки, использующий Mutex для обеспечения реентерабельности. -
Dir.chdirс блоком -
любые операции записи в
IO, когдаIO#syncимеет значение false; в том числе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 позволяет обойти невозможность использовать Mutex внутри обработчика сигнала -
Signal.trapбезопасно использовать внутри блоков, переданных вSignal.trap -
арифметические операции с
IntegerиFloat(‘+’, ‘-’, ‘%’, ‘*’, ‘/’)Кроме того, обработчики сигналов не выполняются между двумя последовательными обращениями к локальным переменным, поэтому сокращённые операторы, такие как ‘+=’ и ‘-=’, при использовании с классами
IntegerиFloatв обработчиках сигналов не приводят к гонке данных.
Методы-обёртки системных вызовов, безопасные в Signal.trap
Поскольку Ruby предоставляет обёртки для многих асинхронно-безопасных функций C, соответствующие обёртки для многих методов IO, File, Dir и Socket безопасны.
(Список неполный)
-
Dir.chdir(без аргумента block)
…
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.