I wasn’t planning to write about lock-free programming again, but a commenter named Mike recently asked an interesting question on my Acquire and Release Semantics post from 2012. It’s a question I wondered about years ago, but could never really reconcile until (possibly) now.
A quick recap: A read-acquire operation cannot be reordered, either by the compiler or the CPU, with any read or write operation that follows it in program order. A write-release operation cannot be reordered with any read or write operation that precedes it in program order.
Those rules don’t prevent the reordering of a write-release followed by a read-acquire. For example, in C++, if
std::atomic<int>, and we write:
A.store(1, std::memory_order_release); int b = B.load(std::memory_order_acquire);
…the compiler is free to reorder those statements, as if we had written:
int b = B.load(std::memory_order_acquire); A.store(1, std::memory_order_release);
And that’s fair. Why the heck not? On many architectures, including x86, the CPU could perform this reordering anyway.