0
votes

Bug d'allocator personnalisé avec conteneur STL

J'ai créé un allocator personnalisé qui alloue la mémoire sur la construction et la libérule sur la destruction. (Afin de permettre une allocation rapide / une distribution de distribution). Quand je l'utilise avec un conteneur stl, tout fonctionne bien! Attendu quand j'utilise la méthode d'affiliation ... Je ne comprends pas pourquoi ...

J'ai essayé d'imprimer chaque pointeur alloué / libre, mais tout va bien. xxx

effectivement le crash de programme. Si je commente 'conteneurb.Assign (10, i);', le programme fonctionne. Si je remplace 'conteneurb.Assign (10, i);' par 'conteneurb.emplace_front ();', le programme fonctionne. Si je remplace 'conteneurb.Assign (10, i);' par 'conteneurb.insert (conteneurb.begine (), 10, i);', le programme de programme. Je ne comprends pas pourquoi ...

Erreur avec Clang et GCC: GRATUIT (): Correction des morceaux non triés corrompus Abort (noyau largué)

erreur avec gdb: GRATUIT (): corrompu des morceaux non triés (P>

Signal reçu Sigabrt, abandonné. __Gi_rise (SIG = SIG @ Entry = 6) à ../sysdeps/unix/sysv/linux/raise.c:51 51 ../sysdeps/unix/sysv/linux/raise.c: Aucun fichier ou répertoire de ce type.

mise à jour:

avec meilleur opérateur ==, j'ai maintenant un SIGSEGV: Signal reçu du programme SIGSEGV, défaut de segmentation. 0x000055555555537A dans STD :: __ CXX11 :: _ LIST_BASE> :: _ m_clear (ceci = 0x7FFFFFDCD0) à /usr/include/c++/8/bits/list.tcc:74 74 __Cur = __tmp -> _ m_next;

la sortie Valgrind: xxx


6 commentaires

Crash comme dans? Jeter une exception?


FYI, void pour t dans votre Taille_t Argument Constructeur aboutit à un comportement plus optimiste. Ces mêmes problèmes se produisent ailleurs. Je suis franchement choqué que votre compilateur ne ressort pas des avertissements (qui devraient être traités comme des erreurs) à vous, car le mien le fait certainement.


J'ai remplacé le pour par: pour (auto ptr = static_cast (m_data) + (taille - 1); PTR> = statique_cast (M_DATA); PTR--) M_FREE.PUSH (PTR) ; Mais ne fonctionne pas.


Le programme Crash: Sigabrt


Avez-vous essayé de déboguer, par exemple pour voir comment votre allocation et vos fonctions d'affectation sont réellement appelées? Ou courir sous un outil comme Valgrind?


Valgrind Détection Aucune fuite et Résultat GDB est le suivant: Signal de programme reçu Sigabrt, avorté. __Gi_rise (SIG = SIG @ Entry = 6) à ../sysdeps/unix/sysv/linux/raise.c:51 51 ../sysdeps/unix/sysv/linux/raudise.c: Aucun fichier ou répertoire de ce type. J'ai ajouté une impression dans chaque méthode / constructeur / destructeur, le programme ne collez pas dans la fonction d'allocator.


3 Réponses :


0
votes

m_data est un pointeur sur void . Il y a le pointeur arithmétique effectué sur m_data dans le constructeur de Customallocator dans la ligne suivante. XXX

Selon la norme, pointeur arithmétique sur void * est mal formé. Cela pourrait conduire à un comportement indéfini.

EDIT
L'OP a été mis à jour et il n'y a plus d'arithmétique de pointeur effectué sur un void * . Nous devons donc trouver d'autres causes pour le crash.
Pour Liste Container EMPECE_FRONT n'affecte pas la validité des itérateurs et des références. Mais les deux assigner et effacer invalident toutes les références, les pointeurs et les itérateurs faisant référence aux éléments du conteneur.
Donc, le programme fonctionne avec emplace_front et qui se bloque avec assignez .


8 commentaires

@ DJ4567: édité la réponse en conséquence, offrant une raison éventuelle de l'accident.


Oui, j'ai lu. L'assignation est toujours effectuée sur un conteneur vide, car pour chaque boucle, le conteneur est effacé. Je ne comprends donc pas pourquoi: / Le code est simple et j'ai passé beaucoup de temps à déboguer cela ... c'est bizarre. Lorsque j'appelle la méthode d'attribution, le code appelle le constructeur d'allocator, j'ai trouvé cela bizarre. Pourquoi la méthode d'affectation crée-t-elle un nouvel allocator? : O


Si je remplace 'conteneurb.Assign (10, i);' par 'conteneurb.insert (conteneurb.begine (), 10, i);', il ne fonctionne pas. Donc, le problème ne provient pas d'itérateur ou de référence invalidée, puisque INSERT MATY n'a pas annulé Itérateur et référence.


@ DJ4567: Il semble qu'il y ait plus de problèmes dans votre code. J'ai essayé de déboguer et j'ai constaté que les destructeurs étaient appelés avant même que les boucles pour ont été exécutées. Voir ceci: wandbox.org/permlink/isfklte5ut8pqwic . Et ils sont appelés plus tard aussi.


Oui, car STD :: Liste Créez un nouvel allocator afin de transformer Customallocator à Customallocator >. Nous avons donc du constructeur de copie et déplacez le constructeur appelé. J'ai mis à jour mon code, maintenant j'ai une faute de segmentation au lieu d'un Sigabrt. Je suppose que STD :: Liste n'aime pas l'itérateur qui n'est toujours pas égal, j'ai donc mis à jour l'opérateur ==. Je ne vois aucune chose étrange dans mon code ... je ne comprends pas :(


Maintenant, dans le code mis à jour, si je commente le :: L'opérateur Supprimer dans le destructeur, tous les travaux. Sinon, j'obtiens un SIGSEGV ... c'est très étrange!


Regardez ceci: la méthode de distribution est appelée avec un PTR bizarre: wandbox.org/permlink/ysgo6nbdpn6ryJPK Que faire Vous pensez à cela? Peut-être: l'allocator se compare égal, la liste utilise donc un allocator pour annouez un pointeur attribué à l'autre allocator. Plus tard, l'allocator est supprimé de sorte que l'autre allocateur a un pointeur pendant? Conclusion: L'allocator de la piscine ne fonctionne pas avec STL? Je ne sais pas ... Si vous avez une meilleure idée ....


Regardez aussi ceci: wandbox.org/permlink/zwsm8pihicrofs9s Je ne sais pas comment résoudre ce problème: o



0
votes

Tous les allocateurs depuis C ++ 11 peuvent avoir un état.

Mais tous les allocateurs ont été copiés du même État d'allocator partagé, de sorte que quelque chose attribué par l'allocator d'origine devrait annoncer une copie optimale.

aussi opérateur == et opérateur! = Devrait comparer l'état, les allocateurs ne doivent pas être égaux si l'état est différent.

Ceci peut être mis en œuvre en ayant un pointeur sur une piscine potentiellement partagée dans votre allocateur plutôt que directement d'avoir une piscine dans l'allocateur. (La piscine d'allocator partagée est parfois appelée «arène»).

Avant C ++ 11, les allocateurs n'ont pas pu le faire. Pour que les objets puissent être alloués / distribués, même avec un allocator construit par défaut, ce qui peut être un exemple différent à chaque fois. Dans ce cas, vos opérateurs d'égalité sont corrects, mais vous devez disposer d'une piscine globale plutôt que de possibilité de piscine séparée pour chaque conteneur.


11 commentaires

Je peux mettre le vide * et empiler dans une structure et gérer un pointeur sur cette structure?


Oui, tant que différentes copies du même allocator accéderaient à la même structure.


Mais un allocator qui utilise l'allocateur de copie d'allocator ne partagera pas la même structure. droite?


Oui, les allocateurs de différents types peuvent ne pas avoir de struct partagé, car la structure allouée par l'allocator sera libérée par certains allocator , mais pas par l'allocator . Cependant, ils peuvent également partager la structure.


Dans ce cas, j'ai besoin d'une sorte de carte mondiale qui contient chaque piscine pour chaque type ... mais étant donné que l'allocator multiple de T utilisera la même piscine T, j'ai 2 options: 1 / Ajouter un mutex dans la piscine partagée, Mais dans ce cas, l'efficacité de la piscine est perdue ... de sorte que la piscine devienne inutile. 2 / Gardez un coffre-fort non du fil, mais dans ce cas, je ne peux pas utiliser plusieurs allocator dans un contexte multithread. Droite? As-tu une meilleure idée?


Si votre compilateur / stl est compatible C ++ 11, l'allocator peut avoir un état. Donc, deux instances d'allocator doivent avoir le même pool que si l'un est une copie d'une autre (ou une autre a été attribuée à une autre). Sinon, deux instances d'allocator peuvent avoir des piscines différentes. Ainsi, il vous suffit de vous assurer que la copie de l'allocator copie pointeur sur la piscine (pouvant être obtenue même avec le constructeur de copie par défaut).


Avant C ++ 11, vous auriez vraiment besoin d'utiliser la piscine par type.


Et si vous avez C ++ 17, vous n'avez même pas besoin de mettre en œuvre votre propre allocator. Vous venez d'implémenter votre piscine en héritant std :: pmr :: memory_resource et transmettez-le aux conteneurs qui utiliseraient std :: pmr :: polymorphic_allocator . Les conteneurs avec cet allocator sont disponibles dans l'espace de noms STD :: PMR.


J'utilise C ++ 17, et je connais le Memory_Resource. Mais j'ai un problème avec la ressource de mémoire. Actuellement, j'ai une piscine d'objet (par exemple, une piscine de taille_t, une piscine de char). Je voudrais utiliser un Memory_Resource mais je ne peux pas. Le constructeur de copie de std :: polymorphic_allocator (utilisé avec STD :: Memory_Resource) Do: construit un polymorphic_allocator à l'aide d'autres.Resource () comme la ressource de mémoire sous-jacente. Dans ce cas, je ne peux pas avoir une piscine comme celle-ci. Pour utiliser le Memory_Resource, le Memory_Resource peut permettre à toutes les tailles (mon pool actuel ne peut affecter que la taille d'un type).


Un polymorphic_allocator utilisera la même mémoire_Resource si copié de polymorphic_allocator . Donc, ma piscine actuelle ne peut donc pas fonctionner avec ce système. Droite?


Oui. Ensuite, vous ne devriez pas utiliser Memory_Resource. En fait, j'ai compris que c'était trompeur de dire "Cela peut être mis en œuvre en ayant un pointeur pour potentiellement piscine partagé dans votre allocateur plutôt que d'avoir directement une piscine dans l'allocator". Oui, il est couramment fait, mais vous n'êtes pas obligé de le faire. Comme vous avez défini propagate_on_container _ * traits to faux_type, vous pouvez avoir une piscine directement dans l'allocator. Néanmoins, vous devrez corriger == et! = Opreacteurs, ils doivent être égaux que si l'instance est égale, pas de typeid.



0
votes

Après cette discussion, j'ai mis en place un alloator Stateful, mais j'ai un problème avec STD :: Liste. J'ai posté une question à ce sujet: conteneur STL ne prend pas en charge l'allocator Stateful?


0 commentaires