Reputation: 751
After a release operation A is performed on an atomic object M, the longest continuous subsequence of the modification order of M that consists of:
- Writes performed by the same thread that performed A. (until C++20)
- Atomic read-modify-write operations made to M by any thread. Is known as release sequence headed by A.
Q1: Why do we need the concept of release sequence?
Q2: Is the first item removed in C++20?
Q3: Why read-modify-write operations qualify in a release sequence but pure write operations don't?
What's special about relaxed RMWs that lets them form a chain without being an acquire load and release store? Either in computer-architecture terms, or in C++ language formalism? Or to put it another way, how can hardware support release-sequence semantics for atomic RMWs but have pure stores that break connections?
Upvotes: 6
Views: 248
Reputation: 1290
This is not a complete answer and is based on my notes of my current newbie understanding of the matter. I have picked the bits from some of the comments on this question and also from other sources. I am posting them here in case it helps someone and also myself in clarifying my understanding further.
Q1: Why do we need the concept of release sequence?
This is already answered excellently here What does "release sequence" mean?. The following are points capturing my current understanding.
Q2: Is the first item removed in C++20?
Yes. As explained in the paper P0982R1: Weaken Release Sequences, following were the reasons behind the removal.
That said, this still doesn't fix all outstanding problems with release sequences as highlighted in the paper P0668R2: Revising the C++ memory model.
There is good reason why Herb Sutter says "Non default atomics.. don't go there.". Knowing all these nuances and managing them is expecting just too much from developers. But even if one ventures into the "with caution" territory, there are recommended ways to avoid such surprises. Its best to encapsulate away all non default atomics stuff and manage any change with extra review. This is also anecdotally mentioned in the part 2 of Herb Sutter's talk.
Q3: Why read-modify-write operations qualify in a release sequence but pure write operations don't? What's special about relaxed RMWs that lets them form a chain without being an acquire load and release store?
I don't have much understanding of individual architecture level details of hardware behaviours. All the same following are my conceptual intuition based notes if it helps some one. In order to understand how read-modify-write operations are different from normal stores for being included in a release sequence, I use the following line of thought.
Footnote 1: As pointed out by Peter Cordes it's common to misread the standard text regarding the value that a RMW operation will read. It might seem intuitive that RMW will read the "last written" value from the atomic, but the catch here is that, which was the last written value for this RMW operation, is evident only after this operation is complete. Here complete means in a way that it has found its place in the variable's modification order and all threads have a consensus about it. Which exact order of values the variable's modification order will take is a result of run time non-determinism, and something that we can't in general make assumptions about, even if we have second hand evidence of a certain value being visible. All the same in the use case under discussion, for example in case of keeping a counter, it does not matter where in the modification order a certain increment or decrement landed. What matters is that they keep the count correct. The RMWs should keep the read, modify, write, read, modify, write in lock step and not end up with read, modify, read, modify, write, write which would mess up the count. This much is ensured by the standard text as long as we don't break the semantic value chain by allowing an intervening pure store.
Footnote 2: I have been reading around trying to get my head around the puzzling world of memory ordering. From my current level of understanding I feel the standard tries to formalize some order and visibility promises to the developers because the developers need them for genuine reasons to support important use cases. The promise might be natural for one architecture to provide, depending on the design goals it prioritizes and the mechanisms it uses, while it might not be straightforward for some other architecture, either available today or a future one. Knowing the details of how most architectures fulfill the promise is a good curiosity question, but it in itself should not be used as justification for why the promise is provided by the standard in the first place. What minimal promises are formalized by the standard should be driven by what is the genuine minimal ask from the developer community, not what is already easily available on some architecture. From that perspective I feel how some architecture achieves a certain promise does not have a direct bearing on which promises are granted by the standard. In this attempt of an answer I have tried to intuitively explore the semantic 'oughtas' and 'shouldas' of why a certain promise is supported by the standard the way it is.
Upvotes: 0