Getting / releasing semantics for atomic variables, accessed through the [] operator

Suppose that an array of atomic variables and a class that controls access to this array by overloading the class' [] operator to return a reference to the atomic variable at idx position:

 class MyClass { public: MyClass() { //initalize every array element with nullptr for (auto& it : array) { it = nullptr; } } std::atomic<int*>& operator[](const size_t idx) { //there is some more code here, doing basic safety checks,... return array[idx]; } private: std::array<std::atomic<int*>, 1000> array; } 

We can access array elements as follows:

 MyClass foo(); int *a = foo[0]; int b = 3; foo[1] = &b 

Please note that any access to such an element will be performed by default using memory_order_seq_cst . To change the order of forced memory, you can do the following:

 int *a = foo[0].load(memory_order_acquire); foo[1].store(&b, memory_order_release); 

But how can I change the implementation of the [] operator so that memory_order_acquire used for all reads, and memory_order_release used for all records? The reason I want to do this in the definition of the [] operator is because there are many calls to array elements at many different locations in the source, and I do not want to distribute the used memory to all of them.

EDIT: As discussed in the comments, you can replace the [] operator with a getter and setter. However, this will require that all calls be replaced by an appropriate function; plus I am wondering if this can be done as I described above.

+6
source share
1 answer

You can use an intermediate reference object that is the result of operator[] . This object then applies the load or store operation based on how the object is used in a future expression.

 class MyClass { struct Ref { std::atomic<int *> &ref_; Ref (std::atomic<int *> &r) : ref_(r) {} operator int * () const { return ref_.load(std::memory_order_acquire); } int * operator = (int *ptr) const { ref_.store(ptr, std::memory_order_release); return ptr; } }; public: //... Ref operator[](const size_t idx) { //there is some more code here, doing basic safety checks,... return array[idx]; } //... }; 

Then, the conversion operator or assignment operator will use the restriction on the correct storage device:

 MyClass foo; int *a = foo[0]; // uses conversion operator, load(acquire) int b = 3; foo[1] = &b; // uses assignment operator, store(release) 
+4
source

Source: https://habr.com/ru/post/973223/


All Articles