Comment déboguer ou résoudre le problème de la boucle sans fin et de la corruption de segment impliquant boost :: interprocess managed_shared_memory?

J’ai le message suivant de “première chance d’exception” qui provient d’une DLL que j’ai écrite et qui tourne dans un exécutable que je n’ai pas écrit. C’est-à-dire que la DLL est un plugin. La première fois que cette exception est déclenchée, une tentative d’ouverture d’un fichier de mappage de mémoire partagée échoue. Si j’ignore les exceptions de la première chance et que je cours, l’application se fige ou se bloque éventuellement.

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

Après plusieurs heures, cela semble être dû à un bloc de code qui boucle en boucle jusqu’à ce qu’une condition d’exception attendue s’efface. Il s’avère que si elle ne disparaît jamais, puis, éventuellement, cette exception se transforme en une autre condition d’exception de bas niveau et / ou se transforme en corruption de tas. Tout cela n’a pour but que d’ouvrir une zone de mémoire partagée à l’aide de Boost :: interprocess.

La première chose qui complique les choses, c’est que, dans mon projet basé sur Visual C ++ 2008, la première exception de la première chance boost::interprocess::interprocess_exception n’est pas levée et identifiée comme son emplacement d’origine, car le compilateur Visual C ++ 2008 ne peut pas trouver le code complexe de modèles de boost-flav en question. Cependant, en passant par la vue du langage d’assemblage, j’ai trouvé le code qui explose.

La ligne de niveau supérieur de mon propre code sur laquelle tout commence à aller mal est:

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

La classe managed_shared_memory ci-dessus managed_shared_memory de interprocess_fwd.hpp et fait partie intégrante des API / en-têtes de mémoire partagée boost. Comme il est basé sur un modèle, ce qui précède se développe en une expression de modèle de renforcement boost C ++ longue d’environ 2Kchars, qui est tronquée à différentes longueurs par l’éditeur de liens et le débogueur. Visual C ++ 2008 n’a plus de capacités de débogage de code source, il semble que ces limites soient en jeu.

Par exemple, quand il explose, j’obtiens cette stack d’appels:

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

Aucune fonction source écrite réelle pour l’utilisateur final n’apparaît dans le vidage de stack ci-dessus.

Comment dois-je déboguer ceci? Deuxièmement, y a-t-il un problème connu avec boost-interprocess, avec Visual C ++ 2008? Troisièmement, que fait le code boost ci-dessous et pourquoi faut-il qu’il boucle en boucle?

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

Plus loin, nous arrivons à:

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

Quoi qu’il en soit, n’essayez pas de déboguer ceci à la maison, voici ce qui se passe:

entrez la description de l'image ici

Enfin, en utilisant ma capacité semblable à celle d’un ninja pour parcourir plusieurs millions de lignes de langage d’assemblage, j’ai vaincu les limites du débogueur de Visual C ++ 2008 et j’ai trouvé le code en question.

C’est ce qui create_device(dev... en réalité: create_device(dev...

Un peu de contexte ici: managed_open_or_create_impl.h ligne 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(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(); } } 

Je crois que j’ai eu certains des mêmes problèmes que vous rencontrez. Examinez la fonction “shared_memory_object :: priv_open_or_create” dans “\ boost \ interprocess \ shared_memory_object.hpp”. Au sumt de cette fonction se trouve un autre appel de fonction “create_tmp_and_clean_old_and_get_filename” qui démarre une chaîne de fonctions qui aboutit à la suppression du fichier de mémoire partagée. J’ai fini par déplacer cet appel de fonction plus bas dans la fonction priv_open_or_create autour du début des instructions case. Je crois que j’utilise boost 1.48. Voici la version finale de cette fonction que j’ai modifiée:

 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::ssortingng shmfile; std::ssortingng 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; } 

En passant, si quelqu’un connaît les canaux officiels que je peux utiliser pour essayer de vérifier et d’append ceci pour renforcer, faites-le-moi savoir, je déteste modifier de telles choses sans en connaître tous les effets.

J’espère que cela t’aides!

Boost est plein de choses étonnantes et effrayantes.

Une solution de contournement simple sous Windows pourrait consister à passer à managed_windows_shared_memory au lieu de managed_shared_memory . Vous pouvez résoudre un grand nombre de problèmes de blocage / blocage méchants, et une sorte de problème de blocage / blocage semble être causé, à son tour, par les différences entre le système de fichiers Windows comportement et le comportement du système de fichiers unix, et en particulier, il semble qu’avec boost et managed_shared_memory sous Windows, il soit possible de dépasser les limites de locking du système de fichiers Windows. Je suis informé qu’un effort pour résoudre ce problème a été effectué dans BOost 1.53, mais j’utilise Boost 1.53 et le problème persiste.

Avec managed_shared_memory sous Windows, vous bénéficiez d’une persistance dépassant la vie des applications client ou serveur. Cela peut être souhaitable dans le cas de certaines personnes, la solution de contournement n’est donc pas une solution à ces problèmes.

Cependant, dans mon cas, je n’avais pas vraiment besoin de ça de toute façon, même si j’avais pensé que ce serait pratique, cela s’avère plus pénible que ça ne vaut la peine, du moins avec la mise en œuvre actuelle de Boost sous Windows.

J’aimerais également souligner que la suppression du fichier de mémoire partagée semble être la cause première de la situation de concurrence critique à l’origine du problème rencontré dans la question ci-dessus. Une synchronisation correcte autour de la création et du contrôle, ainsi que la suppression du fichier semblent être essentielles pour une implémentation réelle du système, et en particulier, un problème dévastateur si vous demandez à votre maître (serveur) de supprimer le fichier à mémoire partagée. alors que certains clients l’utilisent encore. Un redémarrage apparaît nécessaire pour effacer le blocage résultant + le désordre du système de fichiers NTFS.

Si je trouve une vraie solution, je la posterai, mais ce qui précède est plus riche que ce que je pourrais trouver ailleurs. Méfiez-vous de managed_shared_memory et envisagez d’utiliser managed_windows_shared_memory et oubliez d’essayer de faire fonctionner l’idée de “mémoire partagée persistante”. Utilisez plutôt managed_windows_shared_memory Windows uniquement.

Résoudre ce problème tout en conservant la classe managed_shared_memory dans mon application signifie probablement managed_shared_memory tout access à l’object managed_shared_memory dans un autre niveau encore de primitives de synchronisation interprocessus ou même avec un mutex d’API Win32 brut. Boost pourrait faire quelque chose d’équivalent, mais introduirait probablement encore plus de complexité accidentelle.

(À part: suis-je le seul ici à penser que Template-All-the-the-Things a été trop utilisé, de manière générale, et en particulier dans Boost, ces jours-ci?)

Mise à jour 2: j’ai trouvé un autre moyen de geler managed_shared_memory , ce qui bloque toute application à partir de laquelle vous l’utilisez. Je ne m’attendais pas à ce qu’il soit si facile de créer des impasses avec Boost, mais c’est assez facile à faire. Le code mutex à l’intérieur de la mise en œuvre gèlera à jamais en attendant qu’un mutex qu’un autre utilisateur de la mémoire partagée gérée soit parti sans avoir été libéré. Ce sumil interminable dans l’attente d’un mutex qui ne sera jamais publié est un autre défaut de conception majeur dans cette implémentation d’interprocessus de renforcement: jusqu’à présent, j’ai compté plusieurs défauts de conception graves, du moins sur les fenêtres. Peut-être que cela fonctionne à merveille sous Linux.

Le code qui présente ceci est la méthode find (), appelée comme ceci:

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

Voici la trace de la stack pour un interblocage mutex (attente interminable, tâche figée):

La seule solution lorsque vous arrivez ici consiste à supprimer la zone de mémoire partagée, après avoir arrêté ou supprimé tous les processus bloqués en attente de ce 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::lock() Line 280 C++ myapp.exe!boost::interprocess::segment_manager,0>,boost::interprocess::iset_index>::priv_get_lock(bool use_lock=true) Line 1340 C++ myapp.exe!boost::interprocess::segment_manager,0>,boost::interprocess::iset_index>::priv_generic_find(const char * name=0x00394290, boost::interprocess::iset_index,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,0>,boost::interprocess::iset_index>::priv_find_impl,boost::interprocess::allocator,boost::interprocess::segment_manager,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290, bool lock=true) Line 728 + 0x25 bytes C++ myapp.exe!boost::interprocess::segment_manager,0>,boost::interprocess::iset_index>::find,boost::interprocess::allocator,boost::interprocess::segment_manager,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290) Line 423 + 0x1e bytes C++ myapp.exe!boost::interprocess::ipcdetail::basic_managed_memory_impl,0>,boost::interprocess::iset_index,8>::find,boost::interprocess::allocator,boost::interprocess::segment_manager,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder name={...}) Line 346 + 0x23 bytes C++ myapp.exe!boost::interprocess::basic_managed_shared_memory,0>,boost::interprocess::iset_index>::find,boost::interprocess::allocator,boost::interprocess::segment_manager,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder name={...}) Line 208 + 0x10 bytes C++ myapp.exe!CCommonMemory::AllocateOrFindAreaMap(const char * name=0x00394290) Line 128 C++