You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nomicon/src/atomics/relaxed.md

44 lines
1.7 KiB

# Relaxed
Now weve got single-threaded mutation semantics out of the way, we can try
reintroducing a second thread. Well have one thread perform a write to the
memory location, and a second thread read from it, like so:
```rust
// Initial state
let mut data = 0;
// Thread 1:
data = 1;
// Thread 2:
println!("{data}");
```
Of course, any Rust programmer will immediately tell you that this code doesnt
compile, and indeed it definitely does not, and for good reason. But suspend
your disbelief for a moment, and imagine what would happen if it did. Lets draw
a diagram, leaving out the reading lines for now:
```text
Thread 1 data Thread 2
╭───────╮ ┌────┐ ╭───────╮
│ = 1 ├╌┐ │ 0 │ ?╌┤ data │
╰───────╯ ├╌┼╌╌╌╌┤ ╰───────╯
└╌┼╌╌╌╌┤
│ 1 │
└────┘
```
Lets try to figure out where the line in Thread 2s access joins up. The rules
from before dont help us much unfortunately since there are no arrows
connecting that operation to anything, so we cant immediately rule anything
out. As a result, we end up facing a situation we havent faced before: there is
_more than one_ potential value for Thread 2 to read.
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
this situation is considered **Undefined Behavior**. For an unsynchronized read
to be acceptable, there has to be _exactly one_ potential value for it to read,
and when there are multiple like in this situation it is considered a data race.
## “Out-of-thin-air” values