2013-05-20 4 views
9

Ich habe die folgende "First-Chance-Ausnahme" Nachricht, die von einer DLL kommt, die ich geschrieben habe, die in einer ausführbaren Datei läuft, die ich nicht geschrieben habe. Das heißt, die DLL ist ein Plugin. Wenn diese Ausnahme zum ersten Mal ausgelöst wird, schlägt ein Versuch fehl, eine Shared Memory-Map-Datei zu öffnen. Wenn ich Ausnahmen der ersten Chance ignoriere und einfach laufe, erstarrt die Anwendung oder stürzt schließlich ab.Wie kann ich das Problem der Endlosschleife und Heap-Beschädigung beheben oder beheben, das boost :: interprocess managed_shared_memory betrifft?

First-chance exception at 0x76a7c41f in notmyexe.exe: Microsoft C++ exception: boost::interprocess::interprocess_exception at memory location 0x002bc644.. 

Nach mehreren Stunden wird es durch einen Block des Codes verursacht werden, die endlos ist, Looping, bis eine erwartete löscht Ausnahmebedingung. Es stellt sich heraus, dass, wenn es nie klar wird, und dann diese Exception schließlich in eine andere Low-Level-Exception-Bedingung und/oder in Heap-Korruption verwandelt wird. All dies ist nur in dem Bemühen, einen gemeinsamen Speicherbereich mit Boost :: Interprocess zu öffnen. Die erste Sache, die Dinge verkompliziert, ist, dass auf meinem Visual C++ 2008-basierten Projekt die erste boost::interprocess::interprocess_exception First-Chance-Ausnahme nicht ausgelöst und als Speicherort identifiziert wird, da der Visual C++ 2008-Compiler den komplexen Boost nicht finden kann - Flavour Templates Code in Frage. Durch das schrittweise Durchlaufen der Assembly Language View habe ich jedoch den Code gefunden, der explodiert.

Die Top-Level-Linie meines eigenen Code, der es auf zu gehen schlecht beginnt, ist:

segment = new managed_shared_memory( open_or_create 
             , MEMORY_AREA_NAME 
             , SHARED_AREA_SIZE);   

Die obige managed_shared_memory Klasse von interprocess_fwd.hpp ist, und ist zu einem festen Bestandteil des Boost-Shared-Memory-API/Kopfzeilen. Da es auf einer Vorlage basiert, erweitert sich das Obige in einen etwa 2Kchars langen C++ - Boost-Vorlagenausdruck, der vom Linker und vom Debugger auf unterschiedliche Länge gekürzt wird. Visual C++ 2008 hat keine Quellcode-Debugging-Funktionen mehr, scheint es, wenn diese Grenzen im Spiel sind.

Zum Beispiel, wenn es explodiert Ich erhalte diesen Call-Stack:

KernelBase.dll!76a7c41f() 
    [Frames below may be incorrect and/or missing, no symbols loaded for KernelBase.dll]  
    KernelBase.dll!76a7c41f() 
> msvcr90d.dll!_malloc_dbg(unsigned int nSize=2290875461, int nBlockUse=264, const char * szFileName=0x01fcb983, int nLine=1962999808) Line 160 + 0x1b bytes C++ 
    8bfc4d89() 

Keine tatsächlichen Endbenutzer Funktionen geschrieben Quelle in dem Stapel erscheinen oben Dump.

Wie soll ich das debuggen? Zweitens, gibt es ein bekanntes Problem mit boost-interprocess, mit Visual C++ 2008? Drittens, was ist der Boost-Code unten und warum muss er endlos loopen?

boost::interprocess::basic_managed_shared_memory<char, 
    boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, 
     boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>, 
     boost::interprocess::iset_index>::basic_managed_shared_memory<char,boo... 

weitere Schichten nach unten, wir bekommen zu:

basic_managed_shared_memory (open_or_create_t, 
           const char *name, size_type size, 
           const void *addr = 0, const permissions& perm = permissions()) 
     : base_t() 
     , base2_t(open_or_create, name, size, read_write, addr, 
       create_open_func_t(get_this_pointer(), 
       ipcdetail::DoOpenOrCreate), perm) 
    {} 

Anyways, versuchen Sie nicht, diese Kinder zu Hause zu debuggen, hier ist was passiert:

enter image description here

Schließlich Mit meiner Ninja-ähnlichen Fähigkeit, einzelne Millionen Zeilen Assemblersprache zu durchlaufen, habe ich die bösen Debugger-Einschränkungen von Visual C++ 2008 besiegt und den Code in qu gefunden estion.

Dies ist, was in der Tat sprengt: create_device<FileBased>(dev....

Einige Kontext hier: managed_open_or_create_impl.h Linie 351 ...

else if(type == DoOpenOrCreate){ 
     //This loop is very ugly, but brute force is sometimes better 
     //than diplomacy. If someone knows how to open or create a 
     //file and know if we have really created it or just open it 
     //drop me a e-mail! 
     bool completed = false; 
     while(!completed){ 
      try{ 
       create_device<FileBased>(dev, id, size, perm, file_like_t()); // <-- KABOOM! 
       created  = true; 
       completed = true; 
      } 
      catch(interprocess_exception &ex){ 
       if(ex.get_error_code() != already_exists_error){ 
        throw; 
       } 
       else{ 
        try{ 
        DeviceAbstraction tmp(open_only, id, read_write); 
        dev.swap(tmp); 
        created  = false; 
        completed = true; 
        } 
        catch(interprocess_exception &e){ 
        if(e.get_error_code() != not_found_error){ 
         throw; 
        } 
        } 
        catch(...){ 
        throw; 
        } 
       } 
      } 
      catch(...){ 
       throw; 
      } 
      thread_yield(); 
     } 
     } 
+1

+1 für das freihändige mürrische Gesicht. –

+0

Es gibt eine Menge zu absorbieren, aber haben Sie die Möglichkeit in Betracht gezogen, dass eine Ausnahme der ersten Chance vollständig gutartig sein kann? –

+0

Ich habe einige Bearbeitungen vorgenommen und einige sehr unprofessionelle Sachen in dieser Frage entfernt. Ich entschuldige mich für jeden, der die Originalversion lesen musste. –

Antwort

2

Ich glaube, ich habe einige der gleichen Probleme hatten Sie haben. Sehen Sie sich die Funktion "shared_memory_object :: priv_open_or_create" in "\ boost \ interprocess \ shared_memory_object.hpp" an. An der Spitze dieser Funktion steht ein weiterer Funktionsaufruf "create_tmp_and_clean_old_and_get_filename", der eine Funktionskette startet, die das Löschen der Shared-Memory-Datei beendet. Ich habe diesen Funktionsaufruf in der priv_open_or_create-Funktion nach unten verschoben, wo die case-Anweisungen beginnen. Ich glaube, ich verwende Boost 1.48. Hier ist die letzte Version dieser Funktion, die ich geändert:

inline bool shared_memory_object::priv_open_or_create 
    (ipcdetail::create_enum_t type, const char *filename, mode_t mode, const permissions &perm) 
{ 
    m_filename = filename; 
    std::string shmfile; 
    std::string root_tmp_name; 

    //Set accesses 
    if (mode != read_write && mode != read_only){ 
     error_info err = other_error; 
     throw interprocess_exception(err); 
    } 

    switch(type){ 
     case ipcdetail::DoOpen: 
      ipcdetail::get_tmp_base_dir(root_tmp_name); 
      shmfile = root_tmp_name; 
      shmfile += "/"; 
      shmfile += filename; 
      m_handle = ipcdetail::open_existing_file(shmfile.c_str(), mode, true); 
     break; 
     case ipcdetail::DoCreate: 
      ipcdetail::create_tmp_and_clean_old_and_get_filename(filename, shmfile); 
      m_handle = ipcdetail::create_new_file(shmfile.c_str(), mode, perm, true); 
     break; 
     case ipcdetail::DoOpenOrCreate: 
     ipcdetail::create_tmp_and_clean_old_and_get_filename(filename, shmfile); 
      m_handle = ipcdetail::create_or_open_file(shmfile.c_str(), mode, perm, true); 
     break; 
     default: 
     { 
      error_info err = other_error; 
      throw interprocess_exception(err); 
     } 
    } 

    //Check for error 
    if(m_handle == ipcdetail::invalid_file()){ 
     error_info err = system_error_code(); 
     this->priv_close(); 
     throw interprocess_exception(err); 
    } 

    m_mode = mode; 
    return true; 
} 

BTW, wenn jemand die offiziellen Kanäle kennt, kann ich durchmachen, um zu versuchen und erhalten diese überprüft und steigern lassen Sie es mich wissen, wie ich Sachen hasse modifizierende wie dies ohne seine volle Wirkung zu kennen.

Hoffe, das hilft!

+0

Ich denke, die Boost-Mailing-Liste wäre ein guter Ort, um dies zu veröffentlichen, da der Autor von Boost :: Interprocess sagen könnte, ob das eine gute allgemeine Lösung wäre. –

0

Boost ist voll von beiden erstaunlich und beängstigend Dinge.

Eine einfache Abhilfe auf Windows, könnte managed_windows_shared_memory statt managed_shared_memory zu wechseln, können Sie eine Vielzahl von schlimmen Sturz lösen kann/hängen Probleme, und eine Art von Crash/hängen Problem verursacht, durch die Unterschiede wiederum zu sein scheint zwischen Windows-Dateisystem Verhalten und Unix-Dateisystem Verhalten, und insbesondere scheint es, dass mit Boost und managed_shared_memory unter Windows ist es möglich, mit Windows-Dateisystem Sperren Einschränkungen zu laufen. Ich bin informiert, dass eine Anstrengung, um damit fertig zu werden, in BOOST 1.53 abgeschlossen wurde, aber ich benutze Boost 1.53 und ich habe immer noch dieses Problem.

Mit regelmäßigen managed_shared_memory unter Windows, erhalten Sie Persistenz über die Lebensdauer der Client-oder Server-Anwendungen. Dies könnte in einigen Fällen wünschenswert sein, daher ist die Problemumgehung für diese Leute keine echte Lösung.

In meinem Fall brauchte ich das sowieso nicht, obwohl ich dachte, es wäre praktisch, es stellt sich heraus, dass es mehr Schmerz als es wert ist, zumindest mit der aktuellen Boost-Implementierung auf Windows.

Ich möchte auch darauf hinweisen, dass das Löschen der gemeinsamen Speicherdatei scheint die Ursache für die Race-Bedingung, die das Problem in der obigen Frage erlebt verursacht wird. Die richtige Synchronisierung beim Erstellen und Überprüfen und Löschen der Datei scheint für eine reale Implementierung des Systems wesentlich zu sein, und insbesondere scheint es ein verheerendes Problem zu sein, wenn Sie Ihren Master (Server) die Shared-Memory-Datei löschen lassen während einige Clients es noch verwenden. Ein Neustart erscheint notwendig, um das resultierende Lock + NTFS-Dateisystem-Chaos zu löschen.

Wenn ich eine echte Lösung finde, werde ich es veröffentlichen, aber das obige ist mehr Informationen, als ich irgendwo anders finden konnte. Seien Sie vorsichtig bei managed_shared_memory und erwägen Sie, managed_windows_shared_memory zu verwenden und vergessen Sie zu versuchen, die "persistent shared memory" Idee funktionieren zu lassen. Verwenden Sie stattdessen nicht persistente Fenster - nur managed_windows_shared_memory.

Lösung, während die managed_shared_memory Klasse in meiner Anwendung beibehalten bedeutet wahrscheinlich alle Zugriff auf das Objekt managed_shared_memory in noch einer anderen Ebene der Interprozess-Synchronisierung Primitiven oder sogar mit einem rohen Win32 API Mutex. Boost könnte etwas Äquivalentes tun, würde aber wahrscheinlich noch mehr zufällige Komplexität einführen.

(Beiseite: Bin ich der einzige hier, dass die Template-All-die-Dinge denkt, ist in den allgemeinen Gebrauch zu weit, und vor allem in Boost, in diesen Tagen?)

Update 2: Ich habe eine alternative Methode zum Einfrieren gefunden managed_shared_memory und diese friert wiederum jede App ein, die Sie verwenden. Ich habe nicht erwartet, dass es so einfach ist, Deadlocks mit Boost zu erzeugen, aber es ist ziemlich einfach zu machen. Der Mutex-Code in der Implementierung wird für immer einfrieren und auf einen Mutex warten, den ein anderer Benutzer des verwalteten gemeinsamen Speichers ohne Freigabe freigegeben hat. Dieser endlose Schlaf, der auf einen Mutex wartet, der nie veröffentlicht wird, ist ein weiterer tiefer Designfehler in dieser Boost-Interprozess-Implementierung, die ich bis jetzt, zumindest unter Windows, einige ernsthafte Designfehler aufgezählt habe. Vielleicht funktioniert es wunderbar unter Linux.

Der Code, zeigt dies die find() Methode ist, wie folgt aufgerufen:

boost::interprocess::managed_shared_memory * segment; 
    std::pair<MyType*, std::size_t> f = segment->find<MyType>(name); 

Hier ist der Stack-Trace für einen Mutex Deadlock (auch bekannt als endlose Warten, gefroren Aufgabe):

Nur Lösung, wenn Sie hier sind, ist es, den freigegebenen Speicherbereich zu löschen, nachdem alle angehaltenen Prozesse, die auf diesen Mutex warten, beendet oder beendet werden.

> myapp.exe!boost::interprocess::winapi::sched_yield() Line 998 C++ 
    myapp.exe!boost::interprocess::ipcdetail::thread_yield() Line 60 + 0xe bytes C++ 
    myapp.exe!boost::interprocess::ipcdetail::spin_mutex::lock() Line 71 C++ 
    myapp.exe!boost::interprocess::ipcdetail::spin_recursive_mutex::lock() Line 91 C++ 
    myapp.exe!boost::interprocess::interprocess_recursive_mutex::lock() Line 161 C++ 
    myapp.exe!boost::interprocess::scoped_lock<boost::interprocess::interprocess_recursive_mutex>::lock() Line 280 C++ 
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_get_lock(bool use_lock=true) Line 1340 C++ 
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_generic_find<char>(const char * name=0x00394290, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0> > > & index={...}, boost::interprocess::ipcdetail::in_place_interface & table={...}, unsigned int & length=1343657312, boost::interprocess::ipcdetail::bool_<1> is_intrusive={...}, bool use_lock=true) Line 854 + 0x11 bytes C++ 
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_find_impl<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290, bool lock=true) Line 728 + 0x25 bytes C++ 
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290) Line 423 + 0x1e bytes C++ 
    myapp.exe!boost::interprocess::ipcdetail::basic_managed_memory_impl<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index,8>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...}) Line 346 + 0x23 bytes C++ 
    myapp.exe!boost::interprocess::basic_managed_shared_memory<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...}) Line 208 + 0x10 bytes C++ 
    myapp.exe!CCommonMemory::AllocateOrFindAreaMap(const char * name=0x00394290) Line 128 C++ 
+0

Haben Sie das Update 2-teilig repariert? Mein Consumer-Code wird unbegrenzt schlafen, wenn ich diese MyDeque * Mydeque = segment.find ("dequeName") verwenden. – brownKnight

+0

Nein. Ich dachte, dass jeder Code, der so geschickt geschrieben ist, dass ich ihn nicht lesen kann, nicht die richtige Lösung in meiner Codebasis ist. Auf etwas Dümmere umgeschaltet und weniger schwer zu lesen. DDE. Ja. DDE. In 20xx. –

+0

Ich bin mir nicht sicher, ob Sie dies versucht haben. Meine erste Anwendung wurde auf 64-Bit mit Visual Studio gebaut und meine Consumer-Anwendung war auf 32-Bit. Ich habe beide auf 64 Build geändert und es funktioniert jetzt. Ich hoffe, Sie finden es hilfreich. – brownKnight

Verwandte Themen