Premis: Saya bekerja dengan lingkungan embedded ARM (hampir bare-metal) di mana saya bahkan tidak memiliki C ++ 11 (with std::atomic<int>
) tersedia, jadi harap hindari jawaban seperti " cukup gunakan standar C ++std::atomic<int>
": Saya tidak bisa .
Apakah implementasi ARM dari AtomicInt ini benar? (anggap arsitektur ARM adalah ARMv7-A )
Apakah Anda melihat beberapa masalah sinkronisasi? Apakah itu volatile
wajib / bermanfaat?
// 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)
}
Juga, saya mencoba untuk mencapai beberapa penggunaan kembali kode, itulah sebabnya saya mengisolasi hanya satu fungsi dasar untuk diimplementasikan dalam kode platform-spesifik ( add()
metode di dalam arm/atomic_int.cpp
).
Apakah atomic_int.h
benar-benar portabel karena melintasi berbagai platform / arsitektur / kompiler? Apakah pendekatan ini layak ? (Dengan layak saya maksud layak untuk setiap platform untuk menjamin atomicity dengan menerapkan add()
metode saja ).
di sini adalah implementasi GCC 8.3.1 ARM yang sesuai dari fungsi yang sama. Rupanya, satu-satunya perbedaan nyata adalah kehadiran dmb
sebelum dan sesudah. Apakah mereka benar-benar dibutuhkan dalam kasus saya? Mengapa? Apakah Anda memiliki contoh di mana AtomicInt
(tanpa dmb
) saya gagal?
UPDATE: implementasi tetap, get()
metode yang dihapus untuk memecahkan masalah atomicity dan penyelarasan. Sekarang add()
berperilaku seperti standar fetchAndAdd()
.
volatile
kata kunci dalam C ++ berarti tidak mengoptimalkan melalui variabel. Jadiget()
manfaat metode itu. Padahal, secara umum, volatile akan mendeprycates dalam C ++. Jika sistem Anda tidak dapat melakukan sinkronisasi data 32-bit bawaan, maka Anda tidak punya banyak pilihan selain menggunakan mutex - spinlock.__ATOMIC_INT_H_
) dan nama yang dimulai dengan garis bawah diikuti dengan huruf kapital dicadangkan untuk digunakan oleh implementasi. Jangan menggunakannya dalam kode Anda.atomic
mungkin paling baik tidak digunakan untuk menghindari kebingunganstd::atomic
, meskipun menimbulkan pertanyaan mengapa Anda tidak hanya menggunakannya dalam hal apa pun.__ATOMIC_INT_H_
pengidentifikasi berganti nama .Jawaban:
Jika Anda menggunakan
gcc
mungkin Anda dapat menggunakan Legacy__sync
Built-in Functions for Atomic Memory Access :Menghasilkan :
sumber
gcc
, dan dalam hal apa pun saya tidak ingin mengikat implementasi ke kompiler tertentu. Terima kasih atas petunjuk Anda, setidaknya itu memberi tahu saya bahwaadd()
bagian ARM saya harus benar. Apa perbedaan antaraldxr
danldrex
?dmb
sebelum dan sesudahldrex
/strex
loop.