Durch das Abbrechen eines Threads, für den ein Mutex gesperrt ist, wird der Mutex nicht entsperrt

8

hilft einem Kunden bei einem Problem, das er hat. Ich bin eher ein Sysadmin / DBA-Typ, also kämpfe ich damit, ihnen zu helfen. Sie sagen, es ist ein Fehler im Kernel / in der Umgebung, ich versuche entweder zu beweisen oder zu widerlegen, bevor ich darauf bestehe, dass es in ihrem Code ist oder suche die Unterstützung des Herstellers für das Betriebssystem.

Bei Red Hat und Oracle Enterprise Linux 5.7 (und 5.8) wird die Anwendung in C ++ geschrieben

Das Problem, das auftritt, ist, dass der Hauptthread einen separaten Thread startet, um eine möglicherweise lange laufende TCP-Verbindung () [Client, der mit Server verbindet] auszuführen. Wenn der 'long-running'-Aspekt zu lange dauert, brechen sie den Thread ab und starten einen anderen.

Dies geschieht, weil wir den Status des Serverprogramms nicht kennen:

  • Server-Programm läuft und läuft - & gt; Verbindung wird sofort angenommen.
  • Serverprogramm läuft nicht, Maschine und Netzwerk OK - & gt; Verbindung sofort fehlgeschlagen mit Fehler 'Verbindung verweigert'
  • Maschine oder Netzwerk abgestürzt oder heruntergefahren - & gt; Die Verbindung dauert lange mit Fehler 'Keine Route zum Host' fehlschlagen

Das Problem besteht darin, dass der Thread, der den Mutex gesperrt hat, abgebrochen wird (Mit den Bereinigungshandlern, die eingerichtet wurden, um den Mutex freizuschalten), wird der Mutex manchmal NICHT entsperrt.

Damit bleibt der Haupt-Thread hängen, der versucht, den Mutex zu sperren.

Detaillierte Umgebungsinformationen:

  • glibc-2.5-65
  • glibc-2.5-65
  • libcap-1.10-26
  • kernel-debug-2.6.18-274.el5
  • glibc-headers-2.5-65
  • glibc-common-2.5-65
  • libcap-1.10-26
  • kernel-doc-2.6.18-274.el5
  • kernel-2.6.18-274.el5
  • kernel-headers-2.6.18-274.el5
  • glibc-devel-2.5-65

Code wurde erstellt mit: c ++ -g3 tst2.C -lpthread -o tst2

Jede Beratung und Anleitung wird sehr geschätzt

    
dwaynehoov 10.01.2013, 22:02
quelle

2 Antworten

14

Es ist richtig, dass abgebrochene Threads die Mutexe, die sie enthalten, nicht freischalten. Sie müssen dafür sorgen, dass dies manuell geschieht. Das kann schwierig sein, da Sie sehr vorsichtig sein müssen, um die richtigen Bereinigungsroutinen für jeden möglichen Abbruchpunkt zu verwenden. Angenommen, Sie verwenden pthread_cancel , um den Thread abzubrechen und Cleanup-Handler mit pthread_cleanup_push zu setzen, um die Mutexe zu entsperren. Es gibt einige Alternativen, die Sie vielleicht einfacher ausprobieren können und die daher zuverlässiger sein können.

Wenn Sie RAII zum Entsperren des Mutex verwenden, wird zuverlässiger. Unter GNU / Linux ist pthread_cancel mit einer speziellen Ausnahme vom Typ __cxxabi::__forced_unwind implementiert. Wenn also ein Thread abgebrochen wird, wird eine Exception ausgelöst und der Stack wird abgewickelt. Wenn ein Mutex durch einen RAII-Typ gesperrt ist, wird sein Destruktor garantiert ausgeführt, wenn der Stapel durch eine __forced_unwind -Ausnahme abgewickelt wird. Boost Thread stellt eine portable C ++ - Bibliothek zur Verfügung, die Pthreads umschließt und viel einfacher zu verwenden ist. Es bietet einen RAII-Typ boost::mutex und andere nützliche Abstraktionen. Boost Thread bietet auch einen eigenen "Thread Interrupt" -Mechanismus, der ähnlich wie Pthread cancellation, aber nicht derselbe ist, und Pthread cancellation points (wie connect ) sind keine Boost Thread Unterbrechungspunkte, was für manche Anwendungen hilfreich sein kann. Im Fall Ihres Kunden jedoch, da der Zeitpunkt des Abbrechens darin besteht, den connect -Aufruf zu unterbrechen, wollen sie wahrscheinlich bei der Pthread-Löschung bleiben. Der (nicht portable) Weg, dass GNU / Linux die Auslöschung als Ausnahme implementiert, bedeutet, dass es mit boost::mutex gut funktioniert.

Es gibt wirklich keine Entschuldigung für das explizite Sperren und Entsperren von Mutexen, wenn Sie in C ++ schreiben. IMHO ist das wichtigste und nützlichste Feature von C ++ Destruktoren, die ideal sind, um Ressourcen wie z Mutex-Sperren.

Eine andere Option wäre die Verwendung eines robusten Mutex, der durch Aufruf von pthread_mutexattr_setrobust auf einem pthread_mutexattr_t vor der Initialisierung des Mutex. Wenn ein Thread stirbt, während er einen robusten Mutex hält, merkt sich der Kernel, dass der nächste Thread, der versucht, den Mutex zu sperren, den speziellen Fehlercode EOWNERDEAD bekommt. Wenn möglich, kann der neue Thread die durch den Thread geschützten Daten wieder konsistent machen und den Mutex übernehmen. Dies ist viel schwieriger zu verwenden als einfach einen RAII-Typ zu verwenden, um den Mutex zu sperren und zu entsperren.

Ein völlig anderer Ansatz wäre, zu entscheiden, ob Sie wirklich die Mutex-Sperre halten müssen, während Sie connect aufrufen. Halten Mutexe während langsamer Operationen ist keine gute Idee. Kannst du nicht connect aufrufen, wenn der Mutex erfolgreich gesperrt wurde und alle freigegebenen Daten durch den Mutex geschützt werden?

Meine Präferenz wäre, Boost-Thread zu verwenden und den Mutex für längere Zeit nicht zu halten.

    
Jonathan Wakely 10.01.2013, 22:45
quelle
4
  

Das Problem, das auftritt, ist, dass der Hauptthread einen separaten Thread startet, um eine möglicherweise lange laufende TCP-Verbindung () [Client, der mit Server verbindet] auszuführen. Wenn der 'long-running'-Aspekt zu lange dauert, brechen sie den Thread ab und starten einen anderen.

Trivial fix - nicht den Thread abbrechen. Schadet es? Falls nötig, lassen Sie den Thread checken (wenn% code_de schließlich fertig ist), ob die Verbindung noch benötigt wird und, falls nicht, schließen Sie ihn, geben Sie den Mutex frei und beenden Sie ihn. Sie können dies mit einer booleschen Variablen tun, die durch einen Mutex geschützt ist.

Außerdem sollte ein Thread keinen Mutex halten, während er auf Netzwerk-E / A wartet. Mutexe sollten nur für Dinge verwendet werden, die schnell und hauptsächlich CPU-limitiert sind oder vielleicht durch lokale Festplatten begrenzt sind.

Wenn Sie schließlich das Gefühl haben, von außen nach innen greifen und einen Faden zwingen zu müssen, treten Sie zurück. Sie haben den Code für diesen Thread geschrieben. Wenn Sie das Bedürfnis haben, bedeutet das, dass Sie diesen Thread nicht so programmiert haben, dass er das tut, was Sie wirklich tun wollten. Die Lösung besteht darin, den Thread so zu modifizieren, dass er genau das tut, was Sie wirklich wollen. Dann müssen Sie es nicht von außen "herumschieben".

    
David Schwartz 11.01.2013 01:24
quelle

Tags und Links