Предпосылка: я работаю со встроенной (почти голой) средой ARM, где у меня даже нет C ++ 11 (с std::atomic<int>
), поэтому избегайте ответов типа « просто используйте стандартный C ++std::atomic<int>
»: я не могу .
Является ли эта ARM- реализация AtomicInt правильной? (предположим, что архитектура ARM - это ARMv7-A )
Вы видите проблему с синхронизацией? Это volatile
требуется / полезно?
// 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)
}
Кроме того, я пытаюсь добиться некоторого повторного использования кода, поэтому я выделил только одну базовую функцию для реализации в специфичном для платформы коде ( add()
метод внутри arm/atomic_int.cpp
).
Действительно atomic_int.h
ли он переносим на разных платформах / архитектурах / компиляторах? Является ли этот подход представляется возможным ? (Под осуществимостью я имею в виду выполнимость для каждой платформы, чтобы гарантировать атомарность путем реализации только add()
метода ).
вот соответствующая реализация ARM GCC 8.3.1 той же функции. Видимо, единственная реальная разница - это наличие dmb
до и после. Они действительно нужны в моем случае? Почему? У вас есть пример, где мой AtomicInt
(без dmb
) не удается?
ОБНОВЛЕНИЕ: исправлена реализация, удален get()
метод для решения проблем атомарности и выравнивания. Теперь add()
ведет себя как стандарт fetchAndAdd()
.
volatile
Ключевое слово в C ++ означает не оптимизировать с помощью переменной. Таким образом,get()
метод выигрывает от этого. Хотя, в общем, volatile собирается дешифроваться в C ++. Если ваша система не может встроить синхронизацию 32-битных данных, то у вас нет другого выбора, кроме как использовать мьютексы - по крайней мере, спин-блокировки.__ATOMIC_INT_H_
) и имена, начинающиеся с подчеркивания, за которым следует заглавная буква, зарезервированы для использования реализацией. Не используйте их в своем коде.atomic
вероятно, лучше не использовать, чтобы избежать путаницыstd::atomic
, хотя возникает вопрос, почему вы бы не использовали это в любом случае.__ATOMIC_INT_H_
идентификатор.Ответы:
Если вы используете,
gcc
возможно, вы можете использовать устаревшие__sync
встроенные функции для доступа к атомарной памяти :Создает :
источник
gcc
, и в любом случае я не хочу связывать реализацию с каким-либо конкретным компилятором. В любом случае, спасибо за ваш совет, по крайней мере, он говорит мне, что моя ARMadd()
часть должна быть правильной. Какая разница междуldxr
аldrex
?dmb
до и после циклаldrex
/strex
.