Redemption pair not made

I'm thinking about whether an atomic variable can load the old value in a receive-release pair. Suppose we have an atomic variable x, and we save this variable with release semantics, and then load it using receive semantics, is it possible to theoretically read the old value?

std::atomic<int> x = 0; void thread_1() { x.store(1, std::memory_order_release); } void thread_2() { assert(x.load(std::memory_order_acquire) != 0); } 

if thread 1 is finished when thread 2 loads x (so the new value is saved), is it possible for thread 2 to load the old value from x? In other words, if the actual storage before x runs before the load is possible for the assert command to run?

As far as I understood from articles on the Internet, this is possible, but I can’t understand why. The memory fence generated by the repository for x guarantees an empty storage buffer, while memory capture when loading from x is guaranteed to invalidate the cache line, so it must read the updated value.

added

Does this mean that the release itself does not have any compulsory orders? This is just something that was done before the release, will happen before the release, and everything that will be done after the acquisition will happen after it, so a couple of acquisitions-releases ensures the ordering of other operations (why?). Did I understand correctly? Does this mean that the codec below claims it doesn't work

 std::atomic<int> x = 0; std::atomic<int> y = 0; void thread_1() { y.store(1, std::memory_order_relaxed); x.store(1, std::memory_order_release); } void thread_2() { x.load(std::memory_order_acquire); assert(y.load(std::memory_order_relaxed) != 0); } 

Of course, again, if thread 1 was already finished in the store. If we replace x.load with while (x.load () == 0), this will work 100%, but I don't know what makes this work.

And what if I replaced the code with the code below

 std::atomic<int> x = 0; void thread_1() { x.exchange(1, std::memory_order_acq_rel); } void thread_2() { assert(x.exchange(0, std::memory_order_acq_rel) != 0); } 

Has anything changed?

Thanks.

+6
c ++ synchronization atomic c ++ 11 memory-fences
source share
1 answer

You can consider the storage / loading functions with the order of freeing / receiving memory in the form of the following pseudo-code:

 template<class T> struct weak_atomic { void store(T newValue) { ReleaseBarrier(); m_value = newValue; } T load() { T value = m_value; AcquireBarrier(); return value; } volatile T m_value; } 

you said

Storage Fence created by x guarantee empty storage buffer

As I understand it, the release memory barrier will cause the CPU to start its storage buffer, but will execute before , applying the new value to x. Thus, it is possible to read the old value from x by another processor.

In any case, weak atomism is a very complex field. Before you start programming with locking, make sure that you understand the memory barriers.

ADDED

You still seem to be confused with memory barriers. This is a fairly common example of their use.

 volatile int x; volatile bool ok; void thread_1() { x = 100; ok = true; } void thread_2() { if (ok) { assert(x == 100); } } 

Due to non-standard execution, you can get the following sequence:

 thread 1 sets ok to true thread 2 checks ok is true and reads some garbage from x thread 1 sets x to 100 but it is too late 

Another possible sequence:

 thread 2 reads some garbage from x thread 2 checks for ok value 

We can fix this with liberation and acquire memory barriers.

 volatile int x; volatile bool ok; void thread_1() { x = 100; ReleaseBarrier(); ok = true; } void thread_2() { if (ok) { AcquireBarrier(); assert(x == 100); } } 

ReleaseBarrier() ensures that writing to memory cannot jump over the barrier. This means that ok set only to true when x already contains a valid value.

AcquireBarrier() ensures that memory reading cannot jump over the barrier. This means that the value of x is only read after checking the status ok .

This means that you are supposed to use a release / receive pair. We can rewrite this example with my weak_atomic .

 volatile int x; weak_atomic<bool> ok; void thread_1() { x = 100; ok.store(true); } void thread_2() { if (ok.load()) { assert(x == 100); } } 
+5
source share

All Articles