Use “coherence” terminology from the start

pull/378/head
SabrinaJewson 2 years ago
parent b89639939f
commit 59fde6f6bb
No known key found for this signature in database
GPG Key ID: 3D5438FFA5F05564

@ -158,7 +158,7 @@ case that doesnt seem to be enough, since even if atomics were used it still
would have the _option_ of reading `0` instead of `1`, and really if we want our would have the _option_ of reading `0` instead of `1`, and really if we want our
mutex to be sane, it should only be able to read `1`. mutex to be sane, it should only be able to read `1`.
So it seems that want we _want_ is to be able to apply our arrow rules from So it seems that want we _want_ is to be able to apply the coherence rules from
before to completely rule out zero from the set of the possible values — if we before to completely rule out zero from the set of the possible values — if we
were able to draw a large arrow from the Thread 1s `+= 1;` to Thread 2s were able to draw a large arrow from the Thread 1s `+= 1;` to Thread 2s
`guard`, then we could trivially then use the rule to rule out `0` as a value `guard`, then we could trivially then use the rule to rule out `0` as a value
@ -198,7 +198,7 @@ These arrows are a new kind of arrow we havent seen yet; they are known as
_happens-before_ (or happens-after) relations and are represented as thin arrows _happens-before_ (or happens-after) relations and are represented as thin arrows
(→) on these diagrams. They are weaker than the _sequenced before_ (→) on these diagrams. They are weaker than the _sequenced before_
double-arrows (⇒) that occur inside a single thread, but can still be used with double-arrows (⇒) that occur inside a single thread, but can still be used with
the arrow rules to determine which values of a memory location are valid to the coherence rules to determine which values of a memory location are valid to
read. read.
When a happens-before arrow stores a data value to an atomic (via a release When a happens-before arrow stores a data value to an atomic (via a release
@ -299,9 +299,9 @@ Thread 1 locked data Thread 2
└───────┘ └───────┘
``` ```
We can now use the second arrow rule from before to follow _forward_ the arrow We can now use the second coherence rule from before to follow _forward_ the
from the `guard` bubble all the way to the `+= 1;`, determining that it is only arrow from the `guard` bubble all the way to the `+= 1;`, determining that it is
possible for that read to see `0` as its value. only possible for that read to see `0` as its value.
This leads us to the proper memory orderings for any mutex (and other locks like This leads us to the proper memory orderings for any mutex (and other locks like
RW locks too, even): use `Acquire` to lock it, and `Release` to unlock it. So RW locks too, even): use `Acquire` to lock it, and `Release` to unlock it. So

@ -213,6 +213,12 @@ guaranteed by the Abstract Machine:
╰───────╯ └────┘ ╰───────╯ └────┘
``` ```
These two rules combined make up the more generalized rule known as _coherence_,
which is put in place to guarantee that a thread will never see a value earlier
than the last one it read or later than a one it will in future write. Coherence
is basically required for any program to act in a sane way, so luckily the C++20
standard guarantees it as one of its most fundamental principles.
You might be thinking that all this has been is the longest, most convoluted You might be thinking that all this has been is the longest, most convoluted
explanation ever of the most basic intuitive semantics of programming — and explanation ever of the most basic intuitive semantics of programming — and
youd be absolutely right. But its essential to grasp these fundamentals, youd be absolutely right. But its essential to grasp these fundamentals,

@ -28,11 +28,11 @@ Thread 1 data Thread 2
└────┘ └────┘
``` ```
Unfortunately, the rules from before dont help us in finding out where Thread Unfortunately, coherence doesnt help us in finding out where Thread 2s line
2s line joins up to, since there are no arrows connecting that operation to joins up to, since there are no arrows connecting that operation to anything and
anything and therefore we cant immediately rule any values out. As a result, we therefore we cant immediately rule any values out. As a result, we end up
end up facing a situation we havent faced before: there is _more than one_ facing a situation we havent faced before: there is _more than one_ potential
potential value for Thread 2 to read. value for Thread 2 to read.
And this is where we encounter the big limitation with unsynchronized data And this is where we encounter the big limitation with unsynchronized data
accesses: the price we pay for their speed and optimization capability is that accesses: the price we pay for their speed and optimization capability is that
@ -225,7 +225,7 @@ value we loaded. This isnt really a problem — we can just try again and aga
until we succeed, and `compare_exchange` is even nice enough to give us the until we succeed, and `compare_exchange` is even nice enough to give us the
updated value so we dont have to load again. Also note that after weve updated updated value so we dont have to load again. Also note that after weve updated
our value of the atomic, were guaranteed to never see the old value again, by our value of the atomic, were guaranteed to never see the old value again, by
the arrow rules from the previous chapter. the coherence rules from the previous chapter.
So heres how it looks with these changes appplied: So heres how it looks with these changes appplied:

@ -66,8 +66,9 @@ from `Y` and `X` respectively, is it possible for them _both_ to load `false`?
And looking at this diagram, theres absolutely no reason why not. There isnt And looking at this diagram, theres absolutely no reason why not. There isnt
even a single arrow connecting the left and right hand sides so far, so the load even a single arrow connecting the left and right hand sides so far, so the load
has no restrictions on which value it is allowed to pick — and this goes for has no coherence-based restrictions on which value it is allowed to pick — and
both sides equally, so we could end up with an execution like this: this goes for both sides equally, so we could end up with an execution like
this:
```text ```text
a static X c d static Y b a static X c d static Y b

Loading…
Cancel
Save