이 C ++ AtomicInt 구현이 올바 릅니까?


9

전제 : 나는이 내장 된 ARM (거의 베어 메탈 (bare-metal)) 환경에서 일하고 있어요 어디하지 심지어이 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().


volatileC ++의 키워드는 변수를 통해 최적화하지 않음을 의미합니다. 따라서 get()방법은 이점이 있습니다. 일반적으로 휘발성은 C ++에서 deprycates하려고합니다. 시스템이 내장 된 32 비트 데이터를 동기화 할 수없는 경우에는 뮤텍스를 사용하는 것이 가장 좋습니다.
ALX23z

어떤 버전의 arm 아키텍처를 사용하고 있습니까? armv-7?
Mike van Dyke

1
이것은 문제를 해결하지는 않지만 두 개의 연속 밑줄 ( __ATOMIC_INT_H_)을 포함하는 이름과 밑줄로 시작하고 그 뒤에 대문자로 시작하는 이름은 구현에서 사용하도록 예약되어 있습니다. 코드에서 사용하지 마십시오.
Pete Becker

멤버 이름 은와 atomic혼동을 피하는 데 가장 적합하지 std::atomic않지만 어쨌든 왜 그것을 사용하지 않는지 의문을 제기합니다.
Clifford

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하지 않으며 어떤 경우에도 구현을 특정 컴파일러에 바인딩하고 싶지 않습니다. 어쨌든 힌트를 주셔서 감사합니다. 적어도 내 ARM add()부분이 정확해야 한다는 것을 알려줍니다 . 차이 무엇 ldxrldrex?
gentooise

이것은 32 비트 버전 중 하나가 아니라 ARM8 (예 : 64 비트)입니다.
marko

대상 아키텍처를 지정하여 해당 코드를 얻을 수있었습니다 : link . GCC가 실제로 / 루프 dmb전후에있는 것처럼 보입니다 . ldrexstrex
gentooise 2009 년

2
이것이 좋은 접근 방법이라고 생각하지만 컴파일러를 독립적으로 만들 려면 gcc 내장을 사용하여 원하는 기능 으로 godbolt.org/z/WB8rxw 유형으로 이동 하여 해당 어셈블리 출력을 복사하십시오. -march 매개 변수를 특정 버전의 ARM과 일치시켜야합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.