Bu C ++ AtomicInt uygulaması doğru mu?


9

Öncül: C ++ 11 (ile std::atomic<int>) bile bulunmayan bir ARM gömülü (neredeyse çıplak metal) ortamla çalışıyorum , bu yüzden lütfen " sadece standart C ++ kullanstd::atomic<int> " gibi cevaplardan kaçının : Yapamam .

AtomicInt'in bu ARM uygulaması doğru mu? (ARM mimarisinin ARMv7-A olduğunu varsayalım )

Bazı senkronizasyon sorunları görüyor musunuz? Öyle mi volatilegerekli / yararlı?

// 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)
}

Ayrıca, ben bazı kod yeniden elde etmeye çalışıyorum, bu yüzden platforma özgü kod ( add()içindeki yöntem arm/atomic_int.cpp) uygulamak için sadece bir temel işlevi izole .

atomic_int.hgerçekten taşınabilir farklı platformları / mimariler / derleyici genelinde olduğu gibi? Bu yaklaşım uygulanabilir mi? (With uygulanabilir Yani sadece uygulayarak garanti bölünmezlik her platform için uygun add()bir yöntem ).

İşte aynı fonksiyonun karşılık gelen ARM GCC 8.3.1 uygulaması. Görünüşe göre, tek gerçek fark, dmböncesi ve sonrası varlığıdır . Benim durumumda gerçekten gerekli mi? Neden? Benim AtomicInt(olmadan dmb) başarısız olduğu bir örneğiniz var mı?

GÜNCELLEME: sabit uygulama, get()atomisite ve hizalama sorunlarını çözmek için kaldırılan yöntem. Şimdi add()bir standart gibi davranıyor fetchAndAdd().


volatileC ++ içindeki anahtar kelime, değişken üzerinden optimizasyon yapılmaması anlamına gelir. Böylece get()yöntem bundan faydalanır. Bununla birlikte, genel olarak, uçucu C ++ 'da mahrum etmek üzeredir. Sisteminiz 32 bit verileri yerleşik olarak senkronize edemiyorsa, muteksleri kullanmaktan başka bir seçeneğiniz yoktur - en azından spinlock.
ALX23z

Kol mimarisinin hangi sürümünü kullanıyorsunuz? armv-7?
Mike van Dyke

1
Bu soruya yönelik değildir, ancak art arda iki alt çizgi ( __ATOMIC_INT_H_) içeren adlar ve alt çizgi ile başlayan ve büyük harfle başlayan adlar uygulama tarafından ayrılmıştır. Bunları kodunuzda kullanmayın.
Pete Becker

Üye adı atomicmuhtemelen en iyisi std::atomic, karışıklığı önlemek için kullanılmaz , ancak her halükarda neden sadece kullanmamanız gerektiği sorusuna neden olur.
Clifford

ARM mimarisi eklendi, __ATOMIC_INT_H_tanımlayıcı yeniden adlandırıldı .
gentooise

Yanıtlar:


2

Kullanabiliyorsanız gcc, Atomik Bellek Erişimi için Eski __syncYerleşik İşlevleri kullanabilirsiniz :

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

Üretir :

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

Ne yazık ki kullanmıyorum gccve her durumda uygulamayı herhangi bir derleyiciye bağlamak istemiyorum. İpucu için teşekkürler, en azından bana ARM add()bölümümün doğru olması gerektiğini söylüyor. Arasındaki fark nedir ldxrve ldrex?
gentooise

Bu, 32 bit sürümlerden biri yerine ARM8'dir (örn. 64bit).
marko

Hedef mimariyi belirterek karşılık gelen kodu almayı başardım: link . Görünüşe göre GCC aslında / loopundan dmbönce ve sonra koyar . ldrexstrex
gentooise

2
Bu iyi bir yaklaşım olduğunu düşünüyorum ama derleyici bağımsız yapmak için sadece gcc builtins kullanarak istediğiniz işlevi godbolt.org/z/WB8rxw türüne gidin ve ilgili montaj çıktısını kopyalayın. -March parametresini ARM'nin belirli sürümüyle eşleştirdiğinizden emin olun.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.