Vorbehalte bei der Implementierung von Signal.trap-Callbacks
Wie bei der Implementierung von Signal-Handlern in C oder den meisten anderen Sprachen muss aller Code, der an Signal.trap übergeben wird, reentrant sein. Wenn Sie mit Reentrancy nicht vertraut sind, müssen Sie sich auf Wikipedia oder anderswo darüber informieren, bevor Sie den Rest dieses Dokuments lesen.
Am wichtigsten ist, dass „Thread-Sicherheit“ keine Reentrancy garantiert; und Methoden wie Mutex#lock und Mutex#synchronize, die üblicherweise für Thread-Sicherheit verwendet werden, verhindern sogar die Reentrancy.
Ein Implementierungsdetail der Ruby VM
Die Ruby VM verzögert die Ausführung von Signal.trap-Callbacks, bis es für ihre internen Datenstrukturen sicher ist, aber sie weiß nicht, wann es für Datenstrukturen in IHREM Code sicher ist. Ruby implementiert verzögerte Signalbehandlung, indem es kurze C-Funktionen registriert, die nur async-signal-sichere Funktionen als Signal-Handler verwenden. Diese kurzen C-Funktionen tun nur genug, um die VM anzuweisen, die über Signal.trap registrierten Callbacks später im Haupt-Ruby- Thread auszuführen.
Unsichere Methoden zum Aufrufen in Signal.trap-Blöcken
Im Zweifelsfall sollten Sie alles als unsicher betrachten, was nicht als sicher aufgeführt ist.
-
Mutex#lock, Mutex#synchronize und aller Code, der sie verwendet, sind explizit unsicher. Dies schließt
Monitorin der Standardbibliothek ein, der Mutex verwendet, um Reentrancy bereitzustellen. -
Dir.chdirmit Block -
Alle
IO-Schreiboperationen, wennIO#syncfalse ist; einschließlichIO#write,IO#write_nonblock,IO#puts. Pipes und Sockets haben standardmäßig „IO#sync = true“, daher ist es sicher, in sie zu schreiben, es sei denn,IO#syncwurde deaktiviert. -
File#flock, da der zugrunde liegende flock(2)-Aufruf nicht von POSIX spezifiziert ist
Gängige sichere Operationen innerhalb von Signal.trap-Blöcken
-
Zuweisung und Abfrage von lokalen Variablen, Instanz- und Klassenvariablen
-
Die meisten Objektallokationen und Initialisierungen gängiger Typen, einschließlich
Array,Hash,String,Struct,Time. -
Gängige
Array,Hash,String,Struct-Operationen, die keinen Block ausführen, sind im Allgemeinen sicher; aber Vorsicht, wenn anderswo Iteration stattfindet. -
Hash#[],Hash#[]=(es sei denn,Hash.newwurde mit einem unsicheren Block übergeben) -
Thread::Queue#pushundThread::SizedQueue#push(seit Ruby 2.1) -
Die Erstellung eines neuen
ThreadüberThread.new/Thread.start kann verwendet werden, um die Unbrauchbarkeit von Mutexen innerhalb eines Signal-Handlers zu umgehen. -
Signal.trapkann sicher innerhalb von Blöcken aufgerufen werden, die anSignal.trapübergeben werden. -
Arithmetik für
IntegerundFloat(„+“, „-“, „%“, „*“, „/“)Zusätzlich werden Signal-Handler nicht zwischen zwei aufeinanderfolgenden lokalen Variablenzugriffen ausgeführt, sodass Abkürzungen wie „+=“ und „-=“ keinen Datenwettlauf auslösen, wenn sie in Signal-Handlern für
IntegerundFloat-Klassen verwendet werden.
Systemaufruf-Wrapper-Methoden, die innerhalb von Signal.trap sicher sind
Da Ruby Wrapper um viele async-signal-sichere C-Funktionen hat, sind die entsprechenden Wrapper für viele IO, File, Dir und Socket-Methoden sicher.
(Unvollständige Liste)
-
Dir.chdir(ohne Blockargument)
…