क्या यह C ++ AtomicInt कार्यान्वयन सही है?


9

परिसर: मैं एआरएम एम्बेडेड (लगभग नंगे-धातु) वातावरण के साथ काम कर रहा हूं, जहां मेरे पास सी ++ 11 ( std::atomic<int>उपलब्ध) भी नहीं है, इसलिए कृपया " बस मानक सी ++ का उपयोग करेंstd::atomic<int> " जैसे उत्तरों से बचें : मैं नहीं कर सकता

इस एआरएम है कार्यान्वयन AtomicInt का सही? (मान लें कि एआरएम आर्किटेक्चर 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 ++ में घटने वाली है। यदि आपका सिस्टम 32-बिट डेटा को अंतर्निहित नहीं कर सकता है, तो आपके पास म्यूटेक्स - स्पिनलॉक का उपयोग करने के लिए बहुत कम विकल्प हैं।
ALX23z

आर्म आर्किटेक्चर का आप किस संस्करण का उपयोग कर रहे हैं? armv-7?
माइक वैन डाइक

1
यह प्रश्न को संबोधित नहीं करता है, लेकिन ऐसे नाम जिनमें दो लगातार अंडरस्कोर ( __ATOMIC_INT_H_) होते हैं और वे नाम जो एक अंडरस्कोर के साथ शुरू होते हैं, उसके बाद कैपिटल लेटर लागू होता है। उन्हें अपने कोड में उपयोग न करें।
पीट बेकर

सदस्य का नाम atomicशायद सबसे अच्छा नहीं है जिसके साथ भ्रम की स्थिति से बचने के लिए उपयोग किया जाता है std::atomic, हालांकि यह सवाल है कि आप किसी भी मामले में इसका उपयोग क्यों नहीं करेंगे।
क्लिफर्ड

जोड़ा गया एआरएम आर्किटेक्चर, नामांकित __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

यह 32-बिट संस्करणों में से एक के बजाय ARM8 (eg64bit) है।
मार्को

मैं लक्ष्य वास्तुकला को निर्दिष्ट करके संबंधित कोड प्राप्त करने में कामयाब रहा: लिंक । ऐसा लगता है कि जीसीसी वास्तव dmbमें ldrex/ strexलूप से पहले और बाद में डालता है ।
gentooise

2
मुझे लगता है कि यह अच्छा तरीका है, लेकिन इसे संकलक बनाने के लिए स्वतंत्र सिर्फ godbolt.org/z/WB8rxw प्रकार पर जाएं जो फ़ंक्शन आप gcc बिल्डिंस का उपयोग करना चाहते हैं और संबंधित असेंबली आउटपुट की प्रतिलिपि बनाएँ। एआरएम के विशिष्ट संस्करण के साथ -march पैरामीटर का मिलान करना सुनिश्चित करें।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.