Was verursacht diesen Deadlock in meinem Ruby 'Trap' Block?

8

Ich lese gerade Jesse Storimers exzellentes Buch Mit Unix-Prozessen arbeiten . In einem Abschnitt über Trapping-Signale von Kind-Prozessen, die beendet wurden, gibt er ein Codebeispiel.

Ich habe diesen Code leicht modifiziert (siehe unten), um ein bisschen mehr Einblick in das zu bekommen, was passiert:

  • Dass der Elternteil seine eigene Ausführung zwischen Signalen wieder aufnimmt (was ich mit seinem puts sehen kann),
  • Das wait wird für mehrere Kinder in einer trap -Anweisung ausgeführt (manchmal bekomme ich "Empfangen eines CHLD-Signals", gefolgt von mehreren "Kind-PID-Verlassen").

Erwartete Ausgabe

Normalerweise sieht die Ausgabe des folgenden Codes ungefähr so ​​aus:

%Vor%

Der gelegentliche Fehler

Aber ab und zu bekomme ich einen Fehler wie folgt:

%Vor%

Kann mir jemand erklären, was hier schief läuft?

Der Code

%Vor%     
Nathan Long 18.05.2012, 15:47
quelle

1 Antwort

13

Ich habe durch Ruby-Quellen nachgesehen, wo dieser bestimmte Fehler auftritt, und Es wird immer nur ausgelöst, wenn der aktuelle Thread versucht, eine Sperre zu erhalten, aber diese Sperre wird bereits vom aktuellen Thread übernommen. Dies bedeutet, dass die Sperrung nicht einspringend ist:

%Vor%

Jetzt wissen wir wenigstens, was passiert, aber noch nicht warum und wo. Die Fehlermeldung zeigt an, dass dies während des Aufrufs von puts geschieht. Wenn es aufgerufen wird, landet es schließlich in io_binwrite . stdout ist nicht synchronisiert, aber gepuffert, also diese if-Bedingung ist beim ersten Aufruf erfüllt, und ein Puffer plus eine Schreibsperre für diesen Puffer wird eingerichtet. Die Schreibsperre ist wichtig, um die Atomizität von Schreibvorgängen auf stdout zu gewährleisten. Es sollte nicht passieren, dass zwei Threads, die gleichzeitig in stdout schreiben, die Ausgabe des anderen mischen. Um zu zeigen, was ich meine:

%Vor%

Obwohl sich beide Threads beim Schreiben in stdout abwechseln, wird es niemals passieren, dass ein einzelner Schreibvorgang abgebrochen wird - Sie haben immer die vollen 5 a oder b in der Sequenz. Das ist, was die Schreibsperre für gibt.

Was in Ihrem Fall schief läuft, ist eine Race-Bedingung für diese Schreibsperre. Der Elternprozess führt eine Schleife durch und schreibt jede Sekunde in stdout ("Elternteil arbeitet hart"). Aber derselbe Thread führt möglicherweise auch den Block trap aus und versucht erneut, in stdout zu schreiben ("Empfangen eines CHLD-Signals"). Sie können überprüfen, ob es wirklich derselbe Thread ist, indem Sie #{Thread.current} in Ihren puts -Anweisungen hinzufügen. Wenn diese beiden Ereignisse genau genug passieren, haben Sie die gleiche Situation wie im ersten Beispiel: Der gleiche Thread versucht zweimal, die gleiche Sperre zu erhalten, was letztendlich den Fehler auslöst.

    
emboss 18.05.2012, 22:30
quelle