Wie kann ich verkanten Verletzungen in einer Ruby-Erweiterung vermeiden?
-
04-10-2019 - |
Frage
Ich schreibe eine C-Erweiterung, eine Schnittstelle zwischen Ruby und einer asynchronen I / O-Bibliothek bereitstellt. Wenn die Tests über meinen Code ausgeführt wird, ich häufig bekommen Fehler einschließlich (aber nicht beschränkt auf):
[BUG] cross-thread violation in rb_thread_schedule()
Asynchronous IO bedeutet, meine C-Erweiterung benötigt, von mehreren Threads Nachrichten an Rubin liefern (nicht der Haupt Dolmetscher Gewinde). Wie vermeide ich diese Thread-Sicherheit Verletzungen im Prozess?
Lösung
Für Rubin 1.8.x der einzige Weg, um den Fehler ist die offensichtlichste zu vermeiden - nur invoke der Ruby / C API vom Haupt Interpreter-Thread. Ich glaube, dass dies auch in Ruby 1.9.x gilt, aber ich habe nicht mit ihm gearbeitet und weiß nicht, wie seine native Thread-Unterstützung Dinge ändern könnte. Statt mehrere native Threads direkt aufrufen, um die API mit, müssen Sie den Erzeuger / Verbraucher-Muster verwenden Anforderungen von sekundären nativen Threads im Haupt Interpreter Thread, um Ihren Code zu liefern. Und im Idealfall dies tun, während nicht unnötig andere Ruby-grüne Fäden blockiert. Wenn man sich die Ruby-Implementierung aussieht, ist der Rubin grün Thread-Scheduler essentialy eine select()
Schleife. Dies deutet auf die folgende Gesamtstruktur:
- Erstellen Sie ein Rohr oder eine andere IPC-Mechanismus, der eine echte
select()
-able Dateideskriptors bietet. - Spawn native Threads und sie mit dem Schreibende des Rohres sorgen.
- Im Haupt Interpreter Thread Geben eine Ereignisschleife, die auf der Lese
rb_thread_wait_fd()
Ende des Rohres aufruft. Dies wird der Rubin grün Thread-Scheduler ermöglicht andere grüne Fäden laufen zu lassen. - Wenn Sie Ihre sekundären nativen Threads Anfragen für den Haupt-Thread hat, Warteschlange sie sie und auch auf das Rohr schreiben, den grünen Faden Ihre Ereignisschleife aufwacht.
Siehe rb_io_sysread()
(Implementierung von IO#sysread
) für das, was ist wahrscheinlich die einfachste sauber IO-mit-Funktion in der Ruby-Code-Basis.