Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)"



  • @elmut19 Da ich jetzt an einem Problem saß, dass eine ähnliche Symptomatik aufwies musste ich an den Thread hier denken. Ich bin z.B. auch irgendwie mal in so 'nem _Orphan_ptr gelandet (leider nicht deterministisch), aber nicht erst beim Beenden, sondern auch zur Laufzeit.

    Bei mir lag das Problem daran, dass wir Referenzen auf Vektor Elemente gehalten haben. Das hat auch soweit funktioniert, bis der Vektor vergrößert wurde und mehr Kapazität benötigte. Damit waren dann natürlich die Referenzen ungültig und haben ins Nirvana gezeigt.

    Das ganze ist entweder mit 'nem Orpahn_ptr, oder mit 'ner Heap Corruption mir um die Ohren geflogen. Vlt hast du ja eine ähnliche Problematik, nur als Idee, wodran sowas liegen kann.



  • @Schlangenmensch
    Vielen Dank Schlangenmensch für die Info.
    In der Zwischenzeit musste ich mich erstmal einer anderen Sache widmen.
    Nun muss ich wieder zum alten Thema zurück.
    Ich werd in der Richtung weitermachen und mir dieses merkwürdige RSP-Objekt näher ansehen.
    Bei einem "erase()" auf die "map", an der falschen Stelle, passierts bei mir ja auch.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Ich werd in der Richtung weitermachen und mir dieses merkwürdige RSP-Objekt näher ansehen.
    Bei einem "erase()" auf die "map", an der falschen Stelle, passierts bei mir ja auch.

    Versuche auch mal deinen Code einzuengen.

    Hierzu kannst du im ersten Schritt assert() Anweisungen in deinen Code einfügen, welche Dinge zusichern. Ist z.B. die Variable n immer größer 0, aber als int definiert, so kannst du dies mittels assert(n > 0) prüfen.

    Zusätzlich kannst du auch C++ zur Fehlersuche nutzen. So hatte ich beispielsweise alle memset Aufrufe durch eine von mir selbst geschriebene Template memset Funktion ersetzt, welche überprüft ob der Datentyp ein POD ist bzw. ob std::is_standard_layout<T>::value und std::is_trivially_copyable<T> gilt. So entdeckte ich einige memset() Aufrufe auf Klasseninstanzen. Auch eine Anweisung bool operator==(std::wstring&, char* s) = delete;liefert mir schon den ein oder anderen Fehler.

    Ferner musst du versuchen eine Testreihe aufzubauen. Das ganze hat nämlich den Charme dass du Änderungen leicht überprüfen kannst. Hast du eine Änderung vorgenommen, aber deine Testreihe schlägt an, so ist da ein Fehler. Das ist natürlich keine Lösung zu 100%, aber es hilft ungemein beim portieren.

    PS:
    Habe auch mal vor der Aufgabe gestanden 30k C Code nach C++ zu portieren.



  • @Quiche-Lorraine sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    PS:
    Habe auch mal vor der Aufgabe gestanden 30k C Code nach C++ zu portieren.

    rename source.c source.cpp? 🤣



  • @Quiche-Lorraine
    Vielen Dank. Ja, es bleibt noch Einiges zu tun....



  • @DocShoe sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    rename source.c source.cpp?

    🤣

    Mich gruselts ein wenig...

    Nein mein Vorgänger hatte Code von Mikrocontroller auf Windows rübergezogen. Und hierbei war auch eine eigene Heap-Speicherverwaltung inklusive dynamischen Array, Flash Verwaltung, Directory Verwaltung, usw. vorhanden. Da standen also so schöne Sachen wie z.B.

    int CompactEntries(tHandle h, tHandle Name, tHandle Folder, tDirectory d, char Flags);
    

    Und jedesmal nervte es mich herauszufinden was die einzelnen Parameter bedeuteten.

    Ferner war die Heap Verwaltung langsam und buggy. Bis zu einem Megabyte funktionierte alles. Fütterte man die Heap-Verwaltung mit mehr Daten, so kam es zu Datenverlusten und Abstürzen.

    Also habe ich alles konsequent herausgeworfen und durch STL ersetzt. Und siehe da mein Programm verträgt nun auch größere Datensätze im GByte Bereich (Punktwolken).



  • @DocShoe
    Ach ja, ich vergaß. tHandle war natürlich ein unsigned short, eine Art ID des selbst reservierten Blocks. Mittels einer Funktion GetPtr() konnte man zu einem tHandle den entsprechenden Speicherbereich suchen.

    Die eigene Speicherverwaltung nutzte kein new, delete, malloc, calloc, sondern nur eine globale Variable der Form char Data[1024*1024]; und verwaltete diesen selbst, inklusive Allokation, Freigabe, Kompaktierung bzw. Entfragmentierung, usw.

    Hierbei zeigte sich beim portieren einen interresanten Fehler. Wie schon gesagt war tHandle eine Art ID des reservierten Blocks und mittels der Funktion GetPtr() konnte man auf den Speicherbereich zugreifen. Viele Funktionen bekamen als Übergabeparameter ein tHandle z.B. auf ein Punkte-Array. Im ersten Schritt bestimmte dann die Funktion den Zeiger auf das Punkte-Array. Danach arbeitete die Funktion auf der eigenen Speicherverwaltung, s.d. in manchen Fällen die Entfragmentierung anschlug und den kompletten Bereich von Data umkrempelte. Mit dem Ergebnis das der Zeiger auf das Punkte-Array auf einmal ins Nirwana zeigte. Einen ähnlichen Fehler kann man in C++ erzeugen:

    #include <vector>
    #include <iostream>
    
    // Testcode zur Verdeutlichung von Pointer-Problemen.
    int main(void) {
        std::vector<int> L { 1 };
        int const* const p = &L[0];
        
        std::cout << "Erster Wert: " << *p << "\n";
        for (int i = 0; i < 100; i++)
            L.push_back(i);    
        std::cout << "Erster Wert: " << *p << "\n";    
        return 0;
    }
    

    In einem Zwischenschritt ersetzte ich die globale Variable Data durch einzelne std::vector Elemente und peng flog mir der Code um die Ohren. Und dabei entdeckte ich den oben genannten Fehler.



  • @Quiche-Lorraine sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    tHandle war natürlich ein unsigned short

    Hört sich auch alles sehr grausam an.
    Unerklärte Parameter und Methoden sind in meinem Projekt auch haufenweise drin.
    Eigentlich müsste ich inzwischen mit denen vertraut sein. Aber es gibt immer noch Stellen, die ich nicht kappier.
    Der Teil, an dem ich gerade bin, ist einer davon.



  • @Quiche-Lorraine @DocShoe
    Hallo nochmal an alle,
    zum Glück hab ich auch noch ein paar andere Sachen zu tun.
    Aber etwas weiter gekommen bin ich nun doch.

    Ich konnte die komplexen Datentypen in der "map" ausschliessen.
    Ich konnte auch die "insert()" von Elementen in meinen threads ausschliessen!
    Ich kann also Elemente einfügen und diese in "~CAnlage()" bereinigen.
    Auch kann ich dann einzelne Elemente löschen oder einen "clear()" machen.
    Oder ich kann auch einfach das Programm beenden, ohne meine "map" zu löschen.

    Aber es geht schief, sobald ich einen lokalen Iterator im Thread auf diese "map" verwende!
    Dieser Iterator scheint, auch nach Beenden des Thread irgendwas im Speicher zu hinterlassen,
    das das System verwirrt.

    Aber kann mir bitte jemand dabei helfen, wie man so einen Iterator im Thread wieder los wird?
    Im Netz finde ich leider nichts dazu.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Ich konnte die komplexen Datentypen in der "map" ausschliessen.
    Ich konnte auch die "insert()" von Elementen in meinen threads ausschliessen!
    Ich kann also Elemente einfügen und diese in "~CAnlage()" bereinigen.
    Auch kann ich dann einzelne Elemente löschen oder einen "clear()" machen.
    Oder ich kann auch einfach das Programm beenden, ohne meine "map" zu löschen.

    Das war doch schon nach den ersten Seiten dieses Riesen-Threads klar.

    Aber es geht schief, sobald ich einen lokalen Iterator im Thread auf diese "map" verwende!
    Dieser Iterator scheint, auch nach Beenden des Thread irgendwas im Speicher zu hinterlassen,
    das das System verwirrt.

    Bei einer std::map darf nur nicht auf nachträglich gelöschte Elemente per Iterator zugegriffen werden, s.a. Iterator invalidation.
    Wie genau wird denn im Thread dieser Map-Iterator benutzt? Wird dieser gespeichert und nach Map-Änderung (auch aus einem anderen Thread heraus) wieder benutzt?



  • @Th69
    Vielen Dank Th69.
    Mir war es leider nicht klar. Schlimm!

    Der Iterator wird lokal im Thread deklariert.
    Und dann wird die "map" dort damit durchlaufen.
    Und ich dachte immer, dass lokale Deklarationen im Thread-eigenen Stack sind und bei Ende weggeschmissen werden.

    static VOID CClientThread(LPVOID lpvParam)
    {	
    	CString key;
    	CClientThreadCtrl*	p = (CClientThreadCtrl *)lpvParam;
    	std::map<CString,DWORD>::iterator it;
    	...
    	
    	key = ...;
    	it = p->m_pmapDaten->find(key);   // entweder so
            for(it=p->m_pmapDaten->begin; ...){  // oder so, oder beides
               ...
            }
    	...
    }
    
    


  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Und ich dachte immer, dass lokale Deklarationen im Thread-eigenen Stack sind und bei Ende weggeschmissen werden.

    Der Iterator wird auch ordentlich weggeräumt. Die Frage ist: Was passiert mit dem Element, auf das der Iterator verweist. Wenn das Element gelöscht wird (z. B. in einem anderen Thread), dann kannst du den Iterator nicht mehr verwenden.



  • @Schlangenmensch
    Ja, ich weiss, dass dann der eine Iterator kein Element mehr besitzt.
    Diesen Fehler hatte ich ja auch, logischerweise, kann und habe ich aber auch abfangen.

    Der ganze Mist passiert ja auch ohne Element in der "map"!
    Es genügt, dass ich auch einen "find()" auf die leere "map" ausführe und damit
    diesem Iterator einen Wert, abhängig von der "map", zuweise.
    Und dieser Iterator ist lokal in diesem Thread deklariert.

    Und andererseits kann ich aber im Thread ein Element in die "map" einfügen,
    ohne dass es irgendwo Probleme gibt.



  • @elmut19 Du verwirrst mich. Wenn du find() aufrufst und kein Element findest, gibt es den "end()" iterator (zeigt auf ein "Element" hinter de Map).
    Der darf nicht dereferenziert werden.

    Aber, du scheinst das Problem ja ganz gut eingekreist zu haben. Vielleicht kannst du damit jetzt ein minimal Beispiel liefern, an dem sich das Problem demonstrieren lässt.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Diesen Fehler hatte ich ja auch, logischerweise, kann und habe ich aber auch abfangen.

    Wie?



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Und dieser Iterator ist lokal in diesem Thread deklariert.

    Trotzdem greift der Iterator auf Daten außerhalb des Threads zu.

    static VOID CClientThread(LPVOID lpvParam)
    {	
    	CString key;
    	CClientThreadCtrl*	p = (CClientThreadCtrl *)lpvParam;
    	std::map<CString,DWORD>::iterator it;
    	...
    	
    	key = ...;
    	it = p->m_pmapDaten->find(key);   
            // Stell dir hier mal die Frage was alles passieren kann. Annahme der Thread wird
            // hier unterbrochen. Dann könnte doch ein anderer Thread hier m_pmapDaten->clear() 
            // aufrufen oder p->m_pmapDaten auf nullptr setzen, ....
    
    Sleep(2000);  // Provokationstest: Tritt der Fehler nun öfters auf?
            for(it=p->m_pmapDaten->begin; ...){
               ...
            }
    	...
    }
    


  • @wob sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    Diesen Fehler hatte ich ja auch, logischerweise, kann und habe ich aber auch abfangen.

    Wie?

    Ich habe CriticalSections drum rum gebaut und frage den Iterator zusätzlich auf Gültigkeit ab.
    Wenn ich sonst auf ihn zugreife, zeigt er auf "end()".



  • @Schlangenmensch sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    @elmut19 Du verwirrst mich. Wenn du find() aufrufst und kein Element findest, gibt es den "end()" iterator (zeigt auf ein "Element" hinter de Map).
    Der darf nicht dereferenziert werden.

    Aber, du scheinst das Problem ja ganz gut eingekreist zu haben. Vielleicht kannst du damit jetzt ein minimal Beispiel liefern, an dem sich das Problem demonstrieren lässt.

    Ja, zeigt dann einfach nur auf "end()".
    Aber das genügt schon, dass der Fehler dann an anderer Stelle trotzdem auftritt.



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    frage den Iterator zusätzlich auf Gültigkeit ab.

    Wie das?



  • @elmut19 sagte in Zugriffsfehler auf "std::map" in " _Orphan_ptr(const _Nodeptr _Ptr)":

    und frage den Iterator zusätzlich auf Gültigkeit ab.

    Wie machst du das? Wie kann man denn feststellen, ob ein Iterator gültig ist? Wenn genau das Element, auf das der Iterator zeigt, in einem anderen Thread gelöscht wurde zum Beispiel.


Anmelden zum Antworten