Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Acquire-release memory model for two consecutive atomic operations

Following code writes into atomic A, B variable and reads them in opposite order in another thread:

#include <atomic>
#include <thread>

// Initially.
std::atomic<int> A = std::atomic<int>(0);
std::atomic<int> B = std::atomic<int>(0);

// Thread 1
void write()
{
    A.store(1, std::memory_order_release); // line 1
    B.store(1, std::memory_order_release); // line 2
}

// Thread 2
void read()
{
    int valB = B.load(std::memory_order_acquire); // line 3
    int valA = A.load(std::memory_order_acquire); // line 4
}

int main()
{
    std::thread t1(write), t2(read);
    t1.join();
    t2.join();
    return 0;
}

Ideally, given the release-acquire ordering on atomic B, the load on line 3, should synchronize for completion of store on line 1, since it happens to be before release operation on line 2. But the reference only mentions (emphasis, mine) -

All memory writes (non-atomic and relaxed atomic) that happened-before the atomic store from the point of view of thread A, become visible side-effects in thread B.

So, a release-acquire ordering on B, doesn't take into effect the atomic oparations on other variables that aren't relaxed memory ordering?

Is it possible to have then, values for valB = 1, valA = 0, after read() concludes in Thread 2, since B's memory ordering doesn't care about other atomic operations that aren't relaxed? Do we need sequential consistency here?

like image 705
Akash Avatar asked Oct 29 '25 04:10

Akash


1 Answers

Is it possible to have then, values for valB = 1, valA = 0, after read() concludes in Thread 2, since B's memory ordering doesn't care about other atomic operations that aren't relaxed?

No that's not possible. As you quoted:

All memory writes (non-atomic and relaxed atomic) that happened-before the atomic store from the point of view of thread A, become visible side-effects in thread B.

So when you read 1 from B, the happened-before write to A is visible in the reading thread and thus the following read from A must also return 1.

The part in parentheses that you highlighted might be better worded as "(including non-atomic and relaxed atomic)" – is that what tripped you up?

What does the standard say?

The relevant part of the standard would be atomics.order.1.4:

An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M

The meaning of "synchronizes with" is described in intro.multithread:

An evaluation A inter-thread happens before an evaluation B if A synchronizes with B [...]

An evaluation A happens before an evaluation B (or, equivalently, B happens after A) if

  • A is sequenced before B, or
  • A inter-thread happens before B.

Happens-before is transitive: A.store() happens-before (sequenced before) B.store(), happens-before (synchronizes-with) B.load(), happens-before (sequenced before) A.load(). Thus A.store() happens-before A.load().

The standard explicitly states about atomic objects:

If a side effect X on an atomic object M happens before a value computation B of M, then the evaluation B shall take its value from X or from a side effect Y that follows X in the modification order of M.

So, since A.store(1) happens-before A.load() and there are no other candidate operations Y, A.load() must return 1.

I can also recommend this blog post.

like image 82
Chronial Avatar answered Oct 31 '25 18:10

Chronial



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!