2013-05-20 13 views
9

Ho il seguente messaggio di "prima eccezione possibilità" che proviene da una DLL che ho scritto che è in esecuzione in un eseguibile che non ho scritto. Cioè, la DLL è un plugin. La prima volta che si attiva questa eccezione, un tentativo di aprire un file della mappa di memoria condivisa non funziona. Se ignoro le eccezioni di prima scelta e semplicemente eseguo, l'applicazione si blocca o si blocca alla fine.Come faccio a eseguire il debug o correggere il problema del loop infinito e dell'heap heap che coinvolge boost :: interprocess managed_shared_memory?

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

Dopo diverse ore sembra essere causato da un blocco di codice che è un loop all'infinito finché una condizione di eccezione previsto libera. Si scopre che se non si cancella mai, e quindi, alla fine, questa eccezione si trasforma in un'altra condizione di eccezione di basso livello e/o si trasforma in danneggiamento dell'heap. Tutto questo è solo nel tentativo di aprire un'area di memoria condivisa usando Interprocess.

La prima cosa che complica le cose è che sul mio ++ progetto di Visual C 2008 sulla base, la prima eccezione boost::interprocess::interprocess_exception first-chance non è gettato e identificato come il luogo da dove è venuto, perché il Visual C++ 2008 compilatore non riesce a trovare il complesso spinta -file codice template in questione. Comunque, passando per la vista del linguaggio di assemblaggio, ho trovato il codice che esplode.

La linea di livello superiore del mio codice che tutto comincia ad andare male in è:

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

È possibile che questo managed_shared_memory classe è da interprocess_fwd.hpp, ed è una parte standard della spinta condivisa API di memoria/intestazioni. Poiché è basato su template, il precedente si espande in un'espressione di modello di boost C++ lungo 2Kchars, che viene troncato a diverse lunghezze dal linker e dal debugger. Visual C++ 2008 non ha più capacità di debug del codice sorgente, sembra quando questi limiti sono in gioco.

Ad esempio, quando si fa saltare in aria ottengo questo stack di chiamate:

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() 

No effettiva dell'utente finale funzioni sorgenti scritti appaiono nello stack di discarica di cui sopra.

Come si esegue il debug di questo? In secondo luogo, c'è qualche problema noto con boost-interprocess, con Visual C++ 2008? In terzo luogo, che cosa fa il codice di boost qui sotto e perché deve essere ripetuto all'infinito?

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... 

ulteriori strati verso il basso, si arriva a:

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) 
    {} 

In ogni modo, non cercare di eseguire il debug di questo a casa i bambini, ecco cosa succede:

enter image description here

Infine, usando la mia capacità di tipo ninja per un singolo passaggio attraverso diverse milioni di righe di linguaggio assembly ho sconfitto le deboli limitazioni del debugger di Visual C++ 2008 e ho trovato il codice in qu estion.

Questo è ciò che sta esplodendo, infatti: create_device<FileBased>(dev....

Alcuni contesto qui: managed_open_or_create_impl.h linea 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 per la faccia scontrosa a mano libera. –

+0

Qui c'è molto da assorbire, ma hai considerato la possibilità che un'eccezione First-Chance possa essere completamente positiva? –

+0

Ho apportato alcune modifiche e rimosso ciò che era un aspetto non professionale in questa domanda. Mi scuso per chiunque abbia dovuto leggere la versione originale. –

risposta

2

Credo di aver avuto alcuni degli stessi problemi che si verificano. Dai un'occhiata alla funzione "shared_memory_object :: priv_open_or_create" in "\ boost \ interprocess \ shared_memory_object.hpp". Nella parte superiore di questa funzione c'è un'altra chiamata alla funzione "create_tmp_and_clean_old_and_get_filename" che avvia una catena di funzioni che termina con l'eliminazione del file di memoria condivisa. Ho finito per spostare quella chiamata di funzione più in basso nella funzione priv_open_or_create intorno a dove iniziano le dichiarazioni del caso. Credo che sto usando boost 1.48. Ecco la versione finale di quella funzione che ho modificato:

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; 
} 

proposito, se qualcuno conosce i canali ufficiali che posso passare per cercare di ottenere questo verificato e aggiunto per aumentare favore fatemelo sapere come io odio la modifica roba come questo senza conoscere il suo pieno effetto.

Spero che questo aiuti!

+0

Penso che la mailing list di boost sarebbe un buon posto per postare questo come l'autore di Boost :: Interprocess potrebbe dire se sarebbe una buona soluzione generale. –

0

Boost è piena di entrambe le cose sorprendenti e spaventose.

Una semplice soluzione su Windows, potrebbe essere quella di passare a managed_windows_shared_memory invece di managed_shared_memory, è possibile risolvere una serie di dura entrata/appendere i problemi, e una sorta di crash/appendere problema sembra essere causato, a sua volta dalle differenze tra il comportamento del file system di Windows e il comportamento del file system unix, e in particolare, sembra che con boost e managed_shared_memory su Windows, è possibile eseguire un controllo dei limiti di blocco del file system di Windows. Sono informato che uno sforzo per risolvere questo problema è stato completato in BOOST 1.53, ma sto usando Boost 1.53 e ho ancora questo problema.

Con il normale managed_shared_memory su Windows, si ottiene la persistenza oltre la vita di qualsiasi applicazione client o server. Questo potrebbe essere auspicabile nei casi di alcune persone, quindi la soluzione alternativa non è una vera soluzione per quelle persone.

Tuttavia, nel mio caso, non avevo davvero bisogno di quello comunque, anche se avevo pensato che sarebbe stato utile, si è rivelato più doloroso di quanto valesse, almeno con l'attuale implementazione di Boost su Windows.

Vorrei anche sottolineare che la cancellazione del file di memoria condivisa sembra essere la causa principale della condizione di competizione che sta causando il problema riscontrato nella domanda sopra. La corretta sincronizzazione intorno alla creazione e al controllo e la cancellazione del file sembra essere essenziale per un'implementazione del sistema reale e, in particolare, sembra essere un problema devastante, se il tuo master (server) elimina il file di memoria condivisa mentre alcuni client lo stanno ancora utilizzando. Un riavvio appare necessario per cancellare il blocco risultante + il disordine del filesystem NTFS.

Se trovo una soluzione reale, la pubblicherò, ma quanto sopra è più informazioni di quante potrei trovare da nessun'altra parte. Fai attenzione a managed_shared_memory e considera l'utilizzo di managed_windows_shared_memory e dimentica di provare a far funzionare l'idea della "memoria condivisa persistente". Piuttosto, usa managed_windows_shared_memory solo per Windows non persistente.

Risolvendo questa, mantenendo la classe managed_shared_memory nella domanda probabilmente mezzi avvolgitori tutto accesso all'oggetto managed_shared_memory in un altro livello di primitive di sincronizzazione tra processi, o anche con un grezzo Win32 API mutex. Boost potrebbe fare qualcosa di equivalente, ma probabilmente introdurrebbe una complessità ancora più accidentale.

(a parte: Sono l'unico qui che pensa che tale modello-all-le-cose è stata effettuata troppo in uso generale, e soprattutto in Boost, in questi giorni?)

Update 2: Ho trovato un modo alternativo di congelare managed_shared_memory e questo a sua volta blocca qualsiasi app da cui lo si utilizza. Non mi aspettavo che fosse così facile creare deadlock con Boost, ma è piuttosto facile da fare. Il codice mutex all'interno dell'implementazione si bloccherà per sempre in attesa di un mutex che qualche altro utente della memoria condivisa gestita è andato via senza rilasciare. Questo sonno senza fine, in attesa di un mutex che non verrà mai rilasciato, è un altro difetto profondo del design in questa implementazione dell'interprete boost che finora ho contato diversi difetti di progettazione, almeno su Windows. Forse funziona magnificamente su Linux.

Il codice che presenta questo è il metodo find(), chiamato in questo modo:

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

Ecco la traccia dello stack per una situazione di stallo mutex (aka attesa senza fine, compito congelato):

Solo soluzione quando si arriva qui, è quello di eliminare l'area di memoria condivisa, dopo aver fermato o ucciso tutti i processi appesi che sono in attesa di questo mutex.

> 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

Hai forse risolto per caso l'aggiornamento 2 parte ?? Il mio codice utente sta andando a dormire indefinitamente quando utilizzo questo MyDeque * Mydeque = segment.find ("dequeName"). – brownKnight

+0

No. Ho immaginato che qualsiasi codice scritto in modo così intelligente che non riesco a leggerlo, non è la soluzione giusta nella mia base di codici. Passato a qualcosa di più stupido e meno difficile da leggere. DDE. Sì. DDE. Nel 20xx. –

+0

Non sono sicuro di aver provato questo. La mia prima applicazione è stata creata su 64 bit utilizzando Visual Studio e la mia applicazione consumer era su 32 bit. ho cambiato entrambi in 64 build e ora funziona. Spero che lo trovi utile. – brownKnight