Prémisse: je travaille avec un environnement ARM embarqué (presque bare-metal) où je n'ai même pas C ++ 11 (avec std::atomic<int>
) disponible, donc veuillez éviter les réponses comme " utilisez simplement le C ++ standardstd::atomic<int>
": je ne peux pas .
Cette implémentation ARM d'AtomicInt est-elle correcte? (supposons que l'architecture ARM est ARMv7-A )
Voyez-vous un problème de synchronisation? Est-ce volatile
nécessaire / utile?
// File: atomic_int.h
#ifndef ATOMIC_INT_H_
#define ATOMIC_INT_H_
#include <stdint.h>
class AtomicInt
{
public:
AtomicInt(int32_t init = 0) : atom(init) { }
~AtomicInt() {}
int32_t add(int32_t value); // Implement 'add' method in platform-specific file
int32_t sub(int32_t value) { return add(-value); }
int32_t inc(void) { return add(1); }
int32_t dec(void) { return add(-1); }
private:
volatile int32_t atom;
};
#endif
// File: arm/atomic_int.cpp
#include "atomic_int.h"
int32_t AtomicInt::add(int32_t value)
{
int32_t res, prev, tmp;
asm volatile(
"try: ldrex %1, [%3]\n" // prev = atom;
" add %0, %1, %4\n" // res = prev + value;
" strex %2, %0, [%3]\n" // tmp = outcome(atom = res); // may fail
" teq %2, #0\n" // if (tmp)
" bne try" // goto try; /* add failed: someone else modified atom -> retry */
: "=&r" (res), "=&r" (prev), "=&r" (tmp), "+mo" (atom) // output (atom is both in-out)
: "r" (value) // input
: "cc"); // clobbers (condition code register [CPSR] changed)
return prev; // safe return (local variable cannot be changed by other execution contexts)
}
En outre, j'essaie de réutiliser du code, c'est pourquoi j'ai isolé une seule fonction de base à implémenter dans du code spécifique à la plate-forme ( add()
méthode à l'intérieur arm/atomic_int.cpp
).
Est-il atomic_int.h
vraiment portable comme sur différentes plates-formes / architectures / compilateurs? Cette approche est-elle réalisable ? (Avec faisable, je veux dire faisable pour chaque plate-forme pour garantir l'atomicité en mettant en œuvre uniquement la add()
méthode ).
voici l'implémentation ARM GCC 8.3.1 correspondante de la même fonction. Apparemment, la seule vraie différence est la présence d' dmb
avant et d'après. Sont-ils vraiment nécessaires dans mon cas? Pourquoi? Avez-vous un exemple où mon AtomicInt
(sans dmb
) échoue?
MISE À JOUR: implémentation fixe, get()
méthode supprimée pour résoudre les problèmes d'atomicité et d'alignement. Maintenant, il add()
se comporte comme un standard fetchAndAdd()
.
__ATOMIC_INT_H_
) et les noms qui commencent par un trait de soulignement suivi d'une lettre majuscule sont réservés à l'utilisation par l'implémentation. Ne les utilisez pas dans votre code.
atomic
est préférable de ne pas utiliser le nom de membre pour éviter toute confusion std::atomic
, même si cela soulève la question de savoir pourquoi vous ne l'utiliseriez pas de toute façon.
__ATOMIC_INT_H_
identifiant renommé .
volatile
mot-clé en C ++ signifie ne pas optimiser via une variable. Laget()
méthode en profite donc. Bien que, en général, volatile soit sur le point de se déprécier en C ++. Si votre système ne peut pas synchroniser les données 32 bits intégrées, vous n'avez pas d'autre choix que d'utiliser des mutex - spinlock à tout le moins.