Exceptionproblem "this application has requested the runtime to terminate..."



  • Guten Abend.
    Ich habe ein seltsames Problem mit meiner Exceptionklasse. Führe ich den folgenden Code aus, so wird er oft vollständig ausgeführt, manchmal kommt es jedoch zu diesem Fehler:

    this application has requested the runtime to terminate it in an unusual way...
    

    Die Zeile, die den Fehler aufrufen kann, ist das werfen meiner Exception. Lasse ich diese Zeile weg, funktioniert der Code immer.

    Wie kommt der Fehler zustande? Vor allem, warum nur manchmal und nicht immer?

    #include <vector>
    #include <iostream>
    #include <windows.h>
    
    using namespace std;
    
    class Ex {
        public:
            const char* ex;
            Ex( const char* e ) {
                ex = e;
            }
            const char* getMessage() {
                return ex;
            }
    };
    
    class subtest {
        public:
            HANDLE thread;
            int id;
            subtest( int _id ) {
                id = _id;
            }
            void test() {
                throw new Ex( "testexception" ); // Fehlerauslöser
            }
    };
    
    class maintest {
        public:
            vector<subtest*> array;
            int static WINAPI threadfunktion( LPVOID pData ) {
                subtest * st = (subtest*)pData;
                try {
                    st->test();
                } catch( Ex *e ) {
                    cout << "Exception caught: " << e->getMessage() << endl;
                }
                return 1;
            }
            void execute() {
                int count = 1000; // beliebige Anzahl an Threads
                for( int i=0; i<count; i++ ) {
                    subtest * a = new subtest( i );
                    array.push_back( a );
                    array.at(i)->thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)threadfunktion, array.at(i),0, 0 );
                    Sleep(1);
                }
                for( int i=0; i<array.size(); i++ ) {
                    WaitForSingleObject(array.at(i)->thread, INFINITE);
                    CloseHandle( array.at(i)->thread );
                    delete array.at(i);
                }
                array.clear();
            }
    };
    
    int main() {
        maintest *m = new maintest();
        m->execute();
        delete m;
        return 1;
    }
    

    Der Code ist nur ein Beispiel zur Reproduktion des Fehlers und daher möglichst kompakt.


  • Mod

    In meinem VS-2010 wird das Programm korrekt ausgeführt.
    Sowohl Debug als auch Release.

    Du erzeigsu zwar einen Hafen Leaks, weil Du die Exception nicht zerstörst, aber das sollte nicht das Problem sein.



  • Hmm, das riecht doch förmlich nach einem Synchronisationsproblem zwischen den einzelnen Threads. 🙄

    Probiere doch mal bitte zu Debuggingzwecken folgendes aus:

    1.) Kommentiere doch mal probehalber die Zeile delete array.at(i) aus und setze eine Sleep(10000) Anweisung vor die Zeile array.clear();

    2.) Versuche durch aktives Warten/Warten mittels Sleep() in beispielsweise der Funktion test() dein Program gezielt zum Absturz zu bringen. Dazu kannst du aber auch Anzahl der Thread mal probeweise erhöhen.

    3.) Generiere ein Log Ausgabe, in der protokolliert wird, welcher Thread welche Funktion aufruft. Achte unbedingt auf Synchronisationsmechanismen !



  • Erstmal vielen Dank für die Antworten.

    Martin Richter schrieb:

    In meinem VS-2010 wird das Programm korrekt ausgeführt.
    Sowohl Debug als auch Release.

    Wie schon gesagt, läuft bei mir das Programm auch (eigentlich) korrekt, da der Fehler nur manchmal auftritt und nicht bei jeder Ausführung. Zur Reproduktion starte ich das Programm manchmal über 100-mal, bis der Fehler erscheint.

    Martin Richter schrieb:

    Du erzeigsu zwar einen Hafen Leaks, weil Du die Exception nicht zerstörst, aber das sollte nicht das Problem sein.

    Hab ich auch mal behoben, ändert aber (wie du schon sagtest) nichts an dem Problem.

    Bitte ein Bit schrieb:

    Achte unbedingt auf Synchronisationsmechanismen !

    Generell verwende ich (für z.b. Logfiles o.ä) mutexe, die ein zeitgleiches Zugreifen auf z.B. eine Datei verhindern. Da sich aber die test()-Funktion und damit das "throwen" und das Abfangen dieser Exception komplett im Thread lokal abspielt, ging ich davon aus das eine Synchronisation hier nicht nötig ist.

    Bitte ein Bit schrieb:

    3.) Generiere ein Log Ausgabe, in der protokolliert wird, welcher Thread welche Funktion aufruft.

    Der Debugger hält übrigens immer beim "throw" an und sagt:

    In __cxa_throw () ()
    


  • Dann schaue doch mal nach ob zu dem Zeitpunkt dein Objekt überhaupt noch in der Variable array existiert bzw. welche Daten überhaupt noch gültig sind.

    Bei dem Logfile interresiert mich daher nur auf welchen Daten der Thread arbeitet und deren Reihenfolge. Denn da kann man Inkonsistenzen in der Thread-Synchonisation sehen.

    PS:
    Wo wird denn eigentlich deine Exception-Nachricht gespeichert ? Deine Exception-Klasse speichert nur den Zeiger darauf, aber dein Aufruf

    throw new Ex( "testexception" );
    

    legt doch eigentlich die Nachricht nur auf dem Stack an ? 😕 Der Zeiger in deiner Exception-Klasse scheint also irgentwo ins Nirvana zu zeigen 😕


  • Mod

    Nein!
    Das ist vollkommen legal Das ist ein konstanter String. der an den Konstruktor übergeben wird. Dieser Zeiger bleibt gültig. Dann wird das Exception Objekt auf dem Heap erzeugt und geworfen.

    Alles vollkommen legal.



  • Bitte ein Bit schrieb:

    Bei dem Logfile interresiert mich daher nur auf welchen Daten der Thread arbeitet und deren Reihenfolge. Denn da kann man Inkonsistenzen in der Thread-Synchonisation sehen.

    Eigentlich ist das Programm ja so konzipiert, dass die Threads vollkommen unabhängig voneinander laufen (nur lokal Exceptions werfen etc) und das Programm diese Threads nur startet und dann wartet, bis alle beendet sind. Theoretisch müsste ich da doch garnichts synchronisieren weil sich die Threads nirgendwo Speicher teilen, oder vertu ich mich da mit dem const char* in der Exceptionklasse? Weil das Objekt an sich doch für jeden Thread eigens erstellt wird...


  • Mod

    Du vertust Dich nicht mit diesem Zeiger für die Excpetion. Er ist const und er ist ein String der durch den Compiler in einem Code-Segment abgelegt wird. Er ist also für die Dauer der gesamten Laufzeot des Programmes gültig.



  • Er ist also für die Dauer der gesamten Laufzeot des Programmes gültig.

    Danke! Genau das wollte ich wissen. 🙂

    @robinlench:

    Bei solchen Programmen musst du immer damit rechnen das dein Programm an allen möglichen Stellen (auch WinAPI Funktionsaufrufe) unterbrochen werden kann sofern diese nicht atomar sind. Zwischen den einzelnen erzeugten Thread's sehe ich da keine Probleme, aber zwischen den erzeugten Thread's und dem Hauptthread.

    Nächste dumme Frage. Kann es sein das die Anweisung throw nicht atomar ist ? Kann es sein das die Exception zwar geworfen wird und der Thread als beendet markiert wird, er aber gerade in diesem Moment durch den Hauptthread gestört wird, welche gerade dummerweise alle Thread beenden und löschen will ???

    Kann es sein das der Thread beendet und gelöscht wird bevor die entsprechende Exception-Funktion aufgerufen wurde ????



  • Was bedeutet atomar?

    Bitte ein Bit schrieb:

    Kann es sein das der Thread beendet und gelöscht wird bevor die entsprechende Exception-Funktion aufgerufen wurde ????

    Eigentlich nicht, da dies auch den Fehler hervorruft, und zwar instant (bei wenigen 100 Threads).

    void execute() {
                int count = 1000;
                for( int i=0; i<count; i++ ) {
                    subtest * a = new subtest( i );
                    array.push_back( a );
                    array.at(i)->thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)threadfunktion, array.at(i),0, 0 );
                    //Sleep(1); // wird diese Anweisung ausgeführt, reduziert sich die Häufigkeit des Fehlers deutlich
                }
                Sleep(10000); // Hier wird einfach gewartet bis die Threads ziemlich sicher fertig sind
                for( int i=0; i<array.size(); i++ ) {
                    WaitForSingleObject(array.at(i)->thread, INFINITE);
                    CloseHandle( array.at(i)->thread );
                    delete array.at(i);
                }
                array.clear();
            }
    

    Das sollte eigentlich auch zeigen, dass der Mainthread nicht in den Threads rumpfuscht während der Laufzeit (der erste Zugriff auf die Threads nach dem Erstellen ist nach diesen 10 Sekunden, der Fehler kommt aber schon viel früher).

    Ich habe übrigens eine sehr seltene Erscheinung des Fehlers mal festgehalten:
    Es kommen mehrere seiten cryptische Zeichen und es piept mehrmals.

    http://i55.tinypic.com/110g50g.jpg

    Diese ganze Ausgabe kann nur von "cout << "Exception caught: " << e->getMessage() << endl;" kommen, da es die einzige Ausgabe im Code ist. Die Sonderzeichen müssen also der Inhalt einer Exception eines der Threads sein. Nach der Ausgabe kam auch die Meldung "... funktioniert nicht mehr und muss beendet werden". (Diese kam auch vorher, nur zusätzlich noch in der Konsole "this application has requested...").


  • Mod

    Dann mach Dein Programm erstmal dicht gegen andere Fehler.
    1. Was passiert bei Dir, wenn sich kein Thread anlegen lässt?
    2. Du benutzt CreateThread aber die CRT. Das erzeugt auch leaks ohne Ende.
    http://blog.m-ri.de/index.php/2007/11/28/createthread-und-die-crt/
    3. Evtl. liegt der Fehler wo ganz anders, CRT? Du greifst zeitgleich aus mehreren Theads auf cout zu. evtl. passiert da der Fehler.



  • Martin Richter schrieb:

    Dann mach Dein Programm erstmal dicht gegen andere Fehler.
    1. Was passiert bei Dir, wenn sich kein Thread anlegen lässt?

    Dafür hab ich auch mal eine Überprüfung eingebaut:

    if( array.at(i)->thread == NULL ) {
                        cout << "Fehler beim Erstellen des Threads.\n";
                        return;
                    }
    

    Martin Richter schrieb:

    2. Du benutzt CreateThread aber die CRT. Das erzeugt auch leaks ohne Ende.
    http://blog.m-ri.de/index.php/2007/11/28/createthread-und-die-crt/

    Nutze jetzt _beginthreadex anstatt von CreateThread, der Fehler kommt immernoch, allerdings eine ganz neue Erscheinung: wieder cryptische Zeichen, Programm reagiert nicht mehr und ein dauerhaftes Piepen (also ca. 5 Pieptöne pro Sekunde, dauerhaft). Hier noch ein Bild:

    http://oi54.tinypic.com/2ykdqa9.jpg

    Martin Richter schrieb:

    3. Evtl. liegt der Fehler wo ganz anders, CRT? Du greifst zeitgleich aus mehreren Theads auf cout zu. evtl. passiert da der Fehler.

    Dazu habe ich einen Mutex angelegt, ändert leider nichts (ausser dass die Ausgabe geordneter ist):

    WaitForSingleObject( mutex, INFINITE );
                    cout << "Exception caught: " << e->getMessage() << endl;
                    ReleaseMutex( mutex );
    

    Hier auch nochmal der aktuelle Code, aus Platzgründen hier: http://pastebin.com/HTJeQ30Q

    Mit diesen Neuerungen tritt der Fehler auch bei jeder Ausführung auf...



  • Eine Funktion ist atomar, wenn diese nicht durch andere Thread's/Prozesse/... unterbrochen werden kann. http://de.wikipedia.org/wiki/Atomare_Operation

    Aber kommen wir zu deinem Problem zurück. Deine Info's sind gut, denn sie zeigen dass das Problem nicht beim Beenden der Thread's liegt. Die Konsole-Ausgabe sieht eher danach aus als wäre der Ausgabestring zerschossen.

    Probiere doch mal bitte aus ob dein Program abstürzt wenn du die Anweisung cout auskommentierst.

    Ähh, deine Mutex Verwendung ist leider falsch. Klar das er nichts macht, denn du erzeugst ihn und wartest später darauf das er in den Signaled Zustand geht, was soweit wie ich weis, dann der Fall ist, wenn der Mutex erzeugt wurde.

    Probiere doch mal den Locking-Mechanism Critical Section http://en.wikipedia.org/wiki/Critical_section um die Anweisung cout.



  • Hier mal wieder eine neue Erscheinung des Fehlers: http://tinypic.com/r/2ex3ksg/7

    Auskommentieren von cout ändert nichts, auch der Critical Section Mechanismus, den ich nun statt dem Mutex eingebaut habe, bringt keine große Veränderung.

    Generell zur Fehlerbeschreibung lässt sich noch sagen, dass die Wahrscheinlichkeit für diesen Fehler mit der Anzahl der Threads steigt. So tritt der Fehler bei 10 Threads eigentlich nie auf, bei 100-1000 aber oft bzw. fast immer.

    kurzes Update: http://pastebin.com/SFfUmQVT



  • Generell zur Fehlerbeschreibung lässt sich noch sagen, dass die Wahrscheinlichkeit für diesen Fehler mit der Anzahl der Threads steigt. So tritt der Fehler bei 10 Threads eigentlich nie auf, bei 100-1000 aber oft bzw. fast immer.

    Seht gut, wir können also den Fehler anständig reproduzieren.

    Hmm, gehe doch mal hin und kompiliere eine Version welche ständig abstürzt und jage sie durch das Program DebugDiag http://www.microsoft.com/downloads/en/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=en. Es würde mich mal interresieren was das Program dazu sagt. Alternativ kannst du auch mal den Application Verifier http://www.microsoft.com/downloads/en/details.aspx?FamilyID=c4a25ab9-649d-4a1b-b4a7-c9d8b095df18&displaylang=en ausprobieren. Aber denke daran das Debugging mittels Application Verifier für dein Program auch wieder auszuschalten, da der Application Verifier sich bei jedem Start des Programm dranhängt auch wenn dieser scheinbar ausgeschaltet ist.

    Alternativ können wir jetzt auch mal nach dem Motto try and test vorgehen. Schreibe mal die Exception um so das diese einfach nichts macht, oder die Exception Nachricht lokal speichert (auch wenn dies der Fehler nicht sein sollte),... Wir wollen dadurch einfach nur den Fehler finden.

    class Ex 
    {
      public:
        char ex[64];
        Ex( const char* e ) 
        {
          strcpy(ex, e);
        }
        const char* getMessage() 
        {
          return ex;
        }
    }
    


  • Die umgeschriebene Klasse ändert auf den ersten Blick erstmal nichts.

    Hier eine Logfile, erstellt mit Debug Diagnostic Tool: http://pastebin.com/09jVpJpM

    Der Exit-Code für die Threads sowie das ganze Programm ist halt immer 0xC0000005. Habe gelesen dass dann irgendwie eine Zugriffsverletzung vorliegt. Genauer kenn ich mich aber so hardwarenah leider nicht aus...

    Edit:
    Habe das ganze testweise in Visual Studio aufgesetzt, klappt einwandfrei... soviel dann zu MinGW. Nach einer solchen tagelangen Fehlersuche fällt mir der Umstieg nicht schwer, danke an alle die geholfen haben, mitzusuchen!


Anmelden zum Antworten