Це правильна реалізація C ++ AtomicInt?


9

Приміщення: я працюю із вбудованим у 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()метод виграє від цього. Хоча, загалом, мінливі ось-ось будуть зображуватися в C ++. Якщо ваша система не може вбудовувати 32-бітні дані синхронізації, то у вас є мало іншого вибору, крім того, щоб використовувати мутекси - щонайменше.
ALX23z

Яку версію архітектури зброї ви використовуєте? armv-7?
Майк ван Дайк

1
Це не стосується питання, але імена, які містять дві послідовні підкреслення ( __ATOMIC_INT_H_) та імена, які починаються з підкреслення, за яким йде велика літера, зарезервовані для використання реалізацією. Не використовуйте їх у своєму коді.
Піт Бекер

Ім'я учасника atomic, мабуть, найкраще не використовувати, щоб уникнути плутанини std::atomic, хоча воно ставить питання, чому ви б не використовували це ні в якому разі.
Кліффорд

Додана архітектура ARM, перейменований __ATOMIC_INT_H_ідентифікатор.
gentooise

Відповіді:


2

Якщо ви використовуєте, gccви можете використовувати застарілі __syncвбудовані функції для доступу до атомної пам'яті :

void add(int volatile& a, int value) {
    __sync_fetch_and_add(&a, value);
}

Створює :

add(int volatile&, int):
.L2:
        ldxr    w2, [x0]
        add     w2, w2, w1
        stlxr   w3, w2, [x0]
        cbnz    w3, .L2
        dmb     ish
        ret

На жаль, я не використовую gcc, і в будь-якому випадку я не хочу прив'язувати реалізацію до якогось конкретного компілятора. Все одно дякую за вашу підказку, принаймні це говорить мені, що моя add()частина зброї повинна бути правильною. Яка різниця між ldxrі ldrex?
gentooise

Це ARM8 (eg64bit), а не одна з 32-бітних версій.
marko

Мені вдалося отримати відповідний код, вказавши цільову архітектуру: посилання . Здається, GCC насправді ставить dmbдо і після циклу ldrex/ strex.
gentooise

2
Я думаю, що це хороший підхід, але щоб зробити його компілятором незалежним, просто перейдіть до типу godbolt.org/z/WB8rxw у потрібній функції, використовуючи вбудовані gcc та скопіюйте відповідний висновок збірки. Обов’язково відповідність параметра -march конкретній версії ARM.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.