Це правильна реалізація 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.