Voraussetzung: Ich arbeite mit einer in ARM eingebetteten (fast Bare-Metal-) Umgebung, in der nicht einmal C ++ 11 (mit std::atomic<int>
) verfügbar ist. Vermeiden Sie daher Antworten wie " Verwendenstd::atomic<int>
Sie einfach Standard-C ++ ": Ich kann nicht .
Ist diese ARM- Implementierung von AtomicInt korrekt? (Angenommen, die ARM-Architektur ist ARMv7-A )
Sehen Sie ein Synchronisationsproblem? Ist es volatile
erforderlich / nützlich?
// 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)
}
Außerdem versuche ich, eine gewisse Wiederverwendung von Code zu erreichen. Deshalb habe ich nur eine Grundfunktion isoliert, die in plattformspezifischem Code implementiert werden soll ( add()
Methode im Inneren arm/atomic_int.cpp
).
Ist es atomic_int.h
wirklich portabel, da es über verschiedene Plattformen / Architekturen / Compiler hinweg ist? Ist dieser Ansatz machbar ? (Mit machbar meine ich machbar für jede Plattform, um Atomizität zu gewährleisten, indem nur die add()
Methode implementiert wird ).
Hier ist die entsprechende ARM GCC 8.3.1-Implementierung derselben Funktion. Anscheinend ist der einzige wirkliche Unterschied das Vorhandensein von dmb
vorher und nachher. Werden sie in meinem Fall wirklich gebraucht? Warum? Haben Sie ein Beispiel, bei dem mein AtomicInt
(ohne dmb
) fehlschlägt?
UPDATE: Feste Implementierung, entfernte get()
Methode zur Lösung von Atomizitäts- und Ausrichtungsproblemen. Jetzt add()
verhält sich das wie ein Standard fetchAndAdd()
.
volatile
Schlüsselwort in C ++ bedeutet, nicht über Variable zu optimieren. Dieget()
Methode profitiert also davon. Im Allgemeinen ist flüchtig, in C ++ zu entziehen. Wenn Ihr System 32-Bit-Daten nicht synchronisieren kann, haben Sie keine andere Wahl, als Mutexe zu verwenden - zumindest Spinlock.__ATOMIC_INT_H_
) enthalten, und Namen, die mit einem Unterstrich gefolgt von einem Großbuchstaben beginnen, sind für die Verwendung durch die Implementierung reserviert. Verwenden Sie sie nicht in Ihrem Code.atomic
wird wahrscheinlich am besten nicht verwendet, um Verwechslungen mit zu vermeidenstd::atomic
, obwohl sich die Frage stellt, warum Sie das auf keinen Fall einfach verwenden würden.__ATOMIC_INT_H_
Bezeichner umbenannt .Antworten:
Wenn Sie
gcc
möglicherweise verwenden, können Sie die integrierten Legacy__sync
-Funktionen für den atomaren Speicherzugriff verwenden :Erzeugt :
quelle
gcc
und möchte die Implementierung auf keinen Fall an einen bestimmten Compiler binden. Trotzdem vielen Dank für Ihren Hinweis, zumindest sagt er mir, dass mein ARM-add()
Teil korrekt sein sollte. Was ist der Unterschied zwischenldxr
undldrex
?dmb
vor und nach derldrex
/strex
-Schleife setzt.