Lire les commentaires à ce sujet réponse , en particulier:
Ce n'est pas parce que vous ne pouvez pas écrire un test qu'il n'est pas cassé. Comportement indéfini qui fonctionne généralement comme prévu (C et C++ en sont remplis), conditions de concurrence, réorganisation potentielle due à un modèle de mémoire faible ... - CodesInChaos il y a 7 heures
@CodesInChaos s'il ne peut pas être reproduit, le code écrit pour "fixer" ne peut pas être testé non plus. Et mettre du code non testé en direct est un crime pire à mon avis - RhysW il y a 5 heures
... me demande-t-il s'il existe de bonnes façons générales de déclencher de manière cohérente et très rare des problèmes de production causés par les conditions de concurrence dans le cas de test.
Après avoir été dans cette entreprise folle depuis environ 1978, après avoir passé presque tout ce temps dans l'informatique en temps réel intégrée, travaillant sur des systèmes multitâches, multithreadés, multi-quoi que ce soit, parfois avec plusieurs processeurs physiques, ayant chassé plus que ma juste part de la race conditions, mon avis est que la réponse à votre question est assez simple.
Non.
Il n'y a pas de bon moyen général de déclencher une condition de concurrence lors des tests.
Votre seul espoir est de les concevoir complètement à partir de votre système.
Quand et si vous trouvez que quelqu'un d'autre en a bourré un, vous devez le tendre une fourmilière, puis le repenser pour l'éliminer. Après avoir conçu son faux pas (prononcé f *** up) hors de votre système, vous pouvez aller le libérer des fourmis. (Si les fourmis l'ont déjà consommé, ne laissant que des os, placez une pancarte disant "C'est ce qui arrive aux gens qui mettent des conditions de course dans le projet XYZ!" Et LAISSEZ-LE ICI.)
Le meilleur outil que je connaisse pour ce genre de problèmes est une extension de Valgrind appelée Helgrind .
Fondamentalement, Valgrind simule un processeur virtuel et exécute votre binaire (non modifié) par-dessus, afin qu'il puisse vérifier chaque accès à la mémoire. À l'aide de ce cadre, le système de surveillance Helgrind appelle pour déduire lorsqu'un accès à une variable partagée n'est pas correctement protégé par un mécanisme d'exclusion mutuelle. De cette façon, il peut détecter une condition de concurrence théorique même si elle ne s'est pas réellement produite.
Intel vend un outil très similaire appelé Intel Inspector .
Ces outils donnent d'excellents résultats mais votre programme sera considérablement plus lent lors de l'analyse.
L'exposition d'un bogue multithread nécessite de forcer différents threads d'exécution à effectuer leurs étapes dans un ordre entrelacé particulier. Habituellement, cela est difficile à faire sans débogage manuel ou manipulation du code pour obtenir une sorte de "poignée" pour contrôler cet entrelacement. Mais le changement de code qui se comporte de manière imprévisible influencera souvent cette imprévisibilité, il est donc difficile à automatiser.
Une astuce intéressante est décrite par Jaroslav Tulach dans Conception API pratique : si vous avez des instructions de journalisation dans le code en question, manipulez le consommateur de ces instructions de journalisation (par exemple un pseudo-terminal injecté) afin qu'il accepte les messages de journal individuels dans un ordre particulier en fonction de leur contenu. Cela vous permet de contrôler l'entrelacement des étapes dans différents threads sans avoir à ajouter quoi que ce soit au code de production qui n'y est pas déjà.
Il n'y a aucun moyen d'être absolument sûr que différents types de comportements indéfinis (en particulier les conditions de course) n'existent pas.
Cependant, il existe un certain nombre d'outils qui présentent un bon nombre de ces situations. Vous pourrez peut-être prouver qu'un problème existe actuellement avec ces outils, même si vous ne pouvez pas prouver que votre correctif est valide.
Quelques outils intéressants à cet effet:
Valgrind est un vérificateur de mémoire. Il trouve des fuites de mémoire, des lectures de mémoire non initialisée, des utilisations de pointeurs pendants et des accès hors limites.
Helgrind est un vérificateur de sécurité des fils. Il trouve les conditions de course.
Les deux fonctionnent par instrumentation dynamique, c'est-à-dire qu'ils prennent votre programme tel quel et l'exécutent dans un environnement virtualisé. Cela les rend discrets, mais lents.
UBSan est un vérificateur de comportement non défini. Il trouve divers cas de comportement indéfini en C et C++, tels que des débordements d'entiers, des décalages hors plage et des choses similaires.
MSan est un vérificateur de mémoire. Il a des objectifs similaires à Valgrind.
TSan est un vérificateur de sécurité des threads. Il a des objectifs similaires à ceux d'Helgrind.
Ces trois sont intégrés au compilateur Clang et génèrent du code au moment de la compilation. Cela signifie que vous devez les intégrer dans votre processus de construction (en particulier, vous devez compiler avec Clang), ce qui les rend beaucoup plus difficiles à configurer initialement que * Grind, mais d'un autre côté, leur temps d'exécution est beaucoup plus faible.
Tous les outils que j'ai énumérés fonctionnent sous Linux et certains d'entre eux sous MacOS. Je ne pense pas que tout travail sur Windows soit encore fiable.
Il semble que la plupart des réponses ici confondent cette question avec "comment détecter automatiquement les conditions de course?" quand la question est vraiment "comment reproduire les conditions de course dans les tests quand je les trouve?"
La façon de le faire est d'introduire la synchronisation dans votre code qui est utilisée uniquement pour les tests. Par exemple, si une condition de concurrence critique se produit lorsque l'événement X se produit entre l'événement A et l'événement B, pour tester votre application, écrivez du code qui attend que l'événement X se produise après l'événement A. Vous aurez probablement besoin d'un moyen pour que vos tests parlent à votre application pour le dire ("hé je teste cette chose, alors attendez cet événement à cet endroit").
J'utilise node.js et mongo, où certaines actions impliquent la création de données cohérentes dans plusieurs collections. Dans ces cas, mes tests unitaires feront un appel à l'application pour lui dire "mettre en place une attente pour l'événement X", et une fois que l'application l'a configuré, le test de l'événement X s'exécutera et les tests indiqueront par la suite l'application ("j'en ai fini avec l'attente de l'événement X") pour que le reste des tests s'exécute normalement.
La réponse ici explique ce type de chose en détail dans le contexte de python: https://stackoverflow.com/questions/19602535/how-can-i-reproduce-the-race-conditions-in-this- code-python-fiable