Premessa: sto lavorando con un ambiente ARM (quasi bare metal) in cui non ho nemmeno C ++ 11 (con std::atomic<int>
) disponibile, quindi per favore evita risposte come " usa solo C ++ standardstd::atomic<int>
": Non posso .
Questa implementazione ARM di AtomicInt è corretta? (supponiamo che l'architettura ARM sia ARMv7-A )
Vedi qualche problema di sincronizzazione? È volatile
richiesto / 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)
}
Inoltre, sto cercando di ottenere un riutilizzo del codice, ecco perché ho isolato solo una funzione di base da implementare nel codice specifico della piattaforma ( add()
metodo all'interno arm/atomic_int.cpp
).
È atomic_int.h
davvero portatile com'è su diverse piattaforme / architetture / compilatori? Questo approccio è fattibile ? (Con fattibile intendo fattibile per ogni piattaforma per garantire atomicità implementando solo il add()
metodo ).
ecco l'implementazione corrispondente di ARM GCC 8.3.1 della stessa funzione. Apparentemente, l'unica vera differenza è la presenza di dmb
prima e dopo. Sono davvero necessari nel mio caso? Perché? Hai un esempio in cui il mio AtomicInt
(senza dmb
) fallisce?
AGGIORNAMENTO: implementazione fissa, get()
metodo rimosso per risolvere i problemi di atomicità e allineamento. Ora si add()
comporta come uno standard fetchAndAdd()
.
__ATOMIC_INT_H_
) e i nomi che iniziano con un carattere di sottolineatura seguito da una lettera maiuscola sono riservati per l'implementazione. Non usarli nel tuo codice.
atomic
Probabilmente il nome del membro non è probabilmente usato per evitare confusione std::atomic
, anche se pone la domanda perché non lo useresti in ogni caso.
__ATOMIC_INT_H_
identificativo rinominato .
volatile
la parola chiave in C ++ significa non ottimizzare tramite variabile. Quindi ilget()
metodo ne trae beneficio. Sebbene, in generale, volatile stia per depricarsi in C ++. Se il tuo sistema non è in grado di sincronizzare i dati a 32 bit incorporati, allora hai poca scelta se non usare i mutex - spinlock almeno.