Multithreaded Exceptions
-
Hallo,
Ich debugge schon sehr lange ein Problem und nun sind mir die Ideen ausgegangen.
Deshalb hoff ich, hier kann mir wer helfen.Hab ein OS-unabhängiges Echtzeitsprachübertragungsprogramm geschrieben, welches ich momentan auf 2 Betriebsystemen teste:
1x Suse Linux 9.0 (gcc 3.3.1)
1x WindowsXP + SP2 (DevCpp 4.9.9.0, gcc >3.2)Ich arbeite unter Linux mit der StandardPthreadLibrary und unter Windows mit der (so glaubte ich) äquivalenten Pthreads-Win32 - POSIX Threads Library.
Natürlich ist mir schon klar, dass Threads auf UNIX bzw. Windowsebene unterschiedlich umgesetzt werden und hab deshalb auch schon einige Mechanismen eingebaut, die das berücksichtigen.
Problem:
ich starte einen Thread (PARENT) direkt nach dem Programmeinstieg und leg den "int main" Thread mit sleep 5sec schlafen.
Der Parent startet nun den nächsten Thread (CHILD) und schläft danach für sagen wir mal 1sec. und beendet sich dann mit einem return;Im Child wird nun zuerst für 2sec geschlafen und danach eine Exception geworfen,
was nun die Fehlermeldung
"This application has requested the Runtime to terminate in an unusual way.",
zur Folge hat, welche natürlich sehr aussagekräftig ist;-)Unter Suse tritt kein Fehler auf.
Unter XP eben diese Fehlermeldung, welche der gcc vom Dev-Cpp 4.9.9.0 automatisch als std Meldung in das executeable einfügt.Wenn der PARENT-Thread nun wartet (2 sec), bis der CHILD Thread seine Exception wirft, tritt das Problem nicht auf, genau so wenig, wie wenn der PARENT-Thread sich sofort nach dem erzeugen des CHILDs beendet.
Also hab ich das Programm so umgestaltet, dass alle Threads von einem MAIN-ParentThread aus erzeugt werden.
dieser MAIN-PARENT-Thread schläft immer solange auf einer Condition-Variable, bis ein signal von irgend einem anderen Thread diesen aufweckt.
Nun wird im MAIN-PARENT ein neuer Thread erzeugt, welcher den vorher übergebenen Funktionspointer aufruft.Kurz und bündig, es gibt keine verwaisten Threads mehr, da der MAIN-PARENT Thread während der ganzen LifeTime des Programms nicht beendet wird.
Ich kann nun aus jedem beliebigen Thread heraus den MAIN-PARENT dazu veranlassen, einen neuen Thread zu erzeugen. Somit müsste im obigen Problem die Relation zwischen Child und Parent aufgehoben bzw. das Problem behoben sein.
Ist leider nicht der Fall.
Genau das gleiche Probelem ist wieder zu beobachten.Aufgefallen ist mir das Problem erst, nachdem ein connect auf nem TCP Socket die IP Adr. nicht gefunden hat, dh. eine Verzögerung von 3sec oder so, hervorgerufen hat.
Sprich Verzögerungen in orphaned Threads + Exception vertragen sich nicht.
Wenn ich die Exception durch ein return(NOT_OK) ersetze und auf dieses Abfrag, gibts kein Probelem. Allerdings will ich das nicht. Zweitens müsste ich ca 20.000 Codezeilen überarbeiten um die Exceptions aus meinem Programm zu entfernen.
Mich würd ja sehr Interessieren, was genau dieser in der Fehlermeldung beschriebene "unusual way" ist bzw. ob schon jmd. ähnliche Probleme hatte.
mfg zoni
-
Willst du eine Exception in einem Thread werfen und in einem anderen fangen? Verstehe ich das richtig? Das geht natürlich nicht.
-
Nein, das is mir schon klar, dass das nicht geht.
Aber danke für die Antwort zu so später Stunde.Das Problem schaut in Pseudocode so aus:
childThreadFunction() { sleep 2 sec; try { throw(1); } catch(int exc) { cout << "catched" << endl; } return; } parentThreadFunction() { run(childThreadFunction); sleep 1 sec; return; } int main() { run(parentThreadFunction); sleep 5 sec; cout << "program works correctly" << endl; return(0); }
Die Meldung "program works correctly" wird unter windows nie gezeigt (nicht mal "catched" wird ausgegeben),
statt dessen fliegt mir in der Sekunde 2 die besagte Fehlermeldung um die Ohren:-((Anmerkung:
Ich hab meine Threads in einer Klasse gekapselt.
Wenn ich run auf eine Funktion aufrufe, wird ein pthread mit einer hilfsfunktion
gestartet, die vereinfacht so aussieht:void *ThreadX::pthread_function(void *param) { xthread *th_hndl = (xthread*)param; try { th_hndl->exit_value_ = (*th_hndl->xthread_func_)(th_hndl); } catch(Exception exc) { cout << "exception in thread" << endl; return((void*)th_hndl->exit_value_); } cout << "normal thread termination" << endl; return((void*)th_hndl->exit_value_); }
Der Funktionspointer wird vorher je nachdem auf die richtige Funktion gesetzt.
Ich kann also gar nicht irgendwo eine Exception verlieren, weil im Notfall sie immer in dieser Hilfsfunktion gefangen wird.Weiter Vorteile dieser Implementierung sind die freie Definition des Typs des Funktionspointers und der übergebene thread_handle, mit dem ich in einem Thread Zugriff auf seine Resourcen hab (zb. exit_value, is_alive usw.)
Ich würd ja noch verstehen wenn man einen thread zum Waisen macht, dass throw
gewisse Probleme bereitet, allerdings ist das bei mir nie der Fall, da das run wie gesagt ein Signal zum MAIN_PARENT_THREAD schickt, der mir den neuen Thread erstellt, wobei der MAIN_PARENT_Thread bis zum Ende alive ist.Weiß vielleicht jmd. was ein throw wirklich macht, ich schätz mal es ist eine reine Sprungmarke (goto) zum nächsten catchblock, aber wer weiß, wer weiß ...
mfg zoni
PS:
Wie kriag I Leerzeichen in des PseudoCode Listing. HTML funtzt nit wirklich
-
Nur so ne Vermutung: sind unter Windows Exceptions nicht ThreadSafe?
PseudoCode Veränderung:
funtzt:
sleepFunction() throw(int) { Sleep(2000); } childThreadFunction() { try { sleepFunction(); throw(1); } catch(int) { cout << "catched" << endl; } }
Allerdings sobald ich die sleepFunction():
sleepFunction() { Sleep(2000); }
deklariere gehts nimma.
auch
sleepFunction() throw(int) { Sleep(2000); throw(1); }
funtzt nit.
Anscheinend wird hier irgend ein jmp Befehl der sich hinter einem throw verbirgt ungültig, während eine Funktion schläft.
der jmp Befehl hat einfach die Aufgabe zum nächsten Catchblock im compilierten Code zu springen, muss also die Catchblock Adresse irgendwo speichern.
Das Irgendwo wird nun durch Sleep ungültig, was ich im funktionierenden Code (1 Bsp.) dadurch umgeh, dass ich ihn durch eine 2 Funktion die mit throw deklariert wurde diese Adresse nach dem Sleep neu initialisiere.
Aber ich glaub, mich jagen schon wieder irgend welche Hirngespenster.
Kann mir irgend jmd. etwas über die Implementierung von Exceptions in Windows XP berichten, was meine Theorie untermauern würde.
Tatsache ist nur, dass es so funtzt und anders nicht.
Außerdem tritt das Problem nur bei Exceptions auf.
Signale sowie return Statements oder irgendwelche Threadsynchronisationsmechanismen funktionieren auch wenn eine Funktion Sleep aufruft.Sprich dürfte kein verrücktgewordener Pointer an meinem Problem schuld sein.
danke schon im voraus
zoni
-
Exceptions sind thread safe unter Windows.
Probier mal, ob das throw 1 / catch(int) überhaupt funktioniert, ohne irgendwelche Threads oder sowas. Bei sowas bin ich immer skeptisch und mache z.B. "int i = 1; throw i;", obwohl das Literal "1" int sein sollte, soweit ich weiß.
-
Hi,
Hab ich eh genau so im Testprogramm implementiert (mit int i Deklaration).
Im eigentlichen Programm arbeite ich mit nem temporären Exception Objekt, welches ich zwecks Testen zu int i werden ließ.
Aufgefallen ist mir auch noch, dass die sleepFunction() mit irgendeinem throw(xxx) deklariert werden kann, um das Programm korrekt ablaufen zu lassen. Lässt man dieses throw in der Deklaration weg, ergibt sich obige Fehlermeldung.
Das Problem liegt wirklich am sterben des Parent-Threads; wenn dieser noch aktiv ist funtzt. Allerdings hab ich meine Threadhierachie ja wie oben schon beschrieben mit einem MAIN_PARENT Thread ausgelegt.
Anscheinend kann man keine Exceptions mehr werfen, wenn man eine Funktion betritt, diese durch Sleep solange anhält bis sich irgend ein Thread beendet und dann noch in der selben ein throw macht.Wie is das eigentlich in Windows wirklich mit verwaisten Threads (orphan).
Unter Linux is es ja so, dass sie vom init thread adoptiert werden und normal weiterlaufen sollten.
-
Probiers halt einfach aus:
int main() { CreateThread(machwas); }
Ich denk, dass das Programm einfach nach ende von main stirbt, ist aber nur eine Vermutung.
-
Wär schön wenns das machen würde.
Tasache is, dass das Programm nach 2 Sekunden abstürzt, also genau zu dem Zeitpunkt, indem im ChildThread ne Exception gworfen wird.
main müsste das Programm aber noch 2 Sekunden länger am leben erhalten.
(sleep 5 sec;)
Wenn "program works correctly" erscheinen würde, würde es ja funtzn. Tuts aber nicht.int main() { run(parentThreadFunction); sleep 5 sec; // hier 5 SEKUNDEN warten cout << "program works correctly" << endl; return(0); }
Blöder Weise gibts auf der Microsoft Entwickler Site keine Auskunft darüber wie Exception im Betriebsystem Source jetzt tatsächlich behandelt werden. (in Bezug auf threads).
Mögliche Fehlerquellen:
- Die Posix Thread implementierung funtzt nit mit Exceptions
- Der Compiler gcc 3.2 > (DevCpp) übersetzt was falsch
- XP is schuldaber am wahrscheinlichsten hab ich einfach ein Brett vor den Augen.
Hat jmd. vielleicht interessante Links zur Implentierung von Threads oder Exceptions in Windows?? Also nicht wie ich damit umgeh sondern wie sie im OS-Source implementiert sind.
PS:
Wie gesagt, dass is ja nur ein Bsp. Programm wo ich den Fehlerfall isoliert hab.
-
Bei mir funktioniert das folgenden Programm einwandfrei (VC++6, VC++7.1, MingGW 3.2.3), Windows XP.
#include <windows.h> #include <iostream> #include <process.h> using namespace std; void grandchildfct(void*) { Sleep(2000); try { throw 1; } catch (int) { cout << "exception caught" << endl; } cout << "grandchildfct ended properly" << endl; } void childfct(void*) { _beginthread(grandchildfct, 0, NULL); Sleep(1000); cout << "childfct ended properly" << endl; } int main() { _beginthread(childfct, 0, NULL); Sleep(5000); cout << "main ended properly" << endl; }
Also hat's wohl was mit der Lib zu tun, und nicht mit dem Exception handling von Windows oder vom Compiler.
-
Hi,
grßes DANKE an musicman!!
Werd wohl die lib rausschmeißen und mit #ifdef std Windowsfunkitonen nehmen,
um den Code OS mäßig zu abstrahieren.Allerdings hab ich wirklich keine Ahnung warum die LIB so an S... produziert.
mfg zoni
Bin gerade drauf gekommen wie man source einbindet:
void danke{} { cout << "DANKE musicman" << endl; }