We know that Rust has 5 kinds of memory ordering. But I've been struggling to understand them for a few days, and I found that some things are difficult to test. So I came to ask, to ensure that my understanding is correct.
Consider the following code:
use std::sync::atomic::{AtomicBool, Ordering};
fn main() {
let flag = AtomicBool::new(false);
let x = 1; //1
let y = 2; //2
let z = 3; //3
flag.store(true, Ordering::Release); //f
let a = 4; //4
let b = 5; //5
let c = 6; //6
println!("x: {}, y: {}, z: {}, a: {}, b: {}, c: {}", x, y, z, a, b, c);
}
I currently understand that Release
guarantees that when this line of code is executed, the code above it in the same thread will definitely be executed, and the results will definitely be in memory, not in registers. For example, let x = 1;
will definitely be executed and the result will be in memory when the f
line is executed.
The question is, Release
only guarantees that when f
is executed, 1, 2, 3 will definitely be executed and the data will be in memory. This means that the order of execution of 1, 2, 3 (the commented code) is not guaranteed. For example, the compiler may execute 2, 3, 1 (the commented code) in that order, just because it may run faster. Or it may execute 3, 2, 1 (the commented code) in that order, just because it may run faster.
However, no matter how it is executed, when the f
line is executed, 1, 2, 3 (the commented code) will definitely be executed, and the data will be in memory, so that when reading x, y, z later, the values of 1, 2, 3 will definitely be obtained, instead of 1,2,3 value is in registers, and the data in memory is still 0.
So Release
only guarantees that when f
is executed, the commented code 1, 2, 3 will definitely be executed, but the order of execution between them is not guaranteed.
There is also the possibility that the code below Release
, 4, 5, 6 (the commented code), Release
only guarantees that when the f
line is executed, 1, 2, 3 (the commented code) will definitely be executed. It doesn't say how the code below f
will be executed. So, there is also the possibility that 4, 5, 6 (the commented code) will be moved above the f
line. Even if 4, 5, 6 (the commented code) are all moved above the f
line, it should not violate the Release
rule, because after moving above, when the f
line is executed, 1, 2, 3 will still be executed. The difference is that 4, 5, 6 (the commented code) will also be executed, because 4, 5, 6 (the commented code) may be moved above the f
line.
There is also the possibility that 4, 5, 6 (the commented code) are not moved above the f
line, but after executing the f
line, the order of execution of 4, 5, 6 (the commented code) is also uncertain. The possible execution orders are:
4, 5, 6.
4, 6, 5.
6, 5, 4.
All three possibilities exist.
This is also the case where I have exhausted all possible situations. And these memory ordering constraints are within a single thread, it's not possible to move a statement from one thread to another.
So, for Acquire
and Release
, it should be the same.
Is that right? If my understanding is correct.
Then Release
should be in a single thread, when the Release
line is executed, the code above Release
will definitely be executed, but the order of execution between them is not guaranteed. The code below Release
may be reordered, not only the order of execution between them, but also the possibility that the entire code is moved above the Release
line.
And Acquire
is the opposite of Release
. Acquire
only guarantees that when the Acquire
line is executed, the code below it will definitely not be executed yet. It's just that the code below Acquire
cannot be moved above the Acquire
line. And the order of execution between the code below Acquire
is not guaranteed. It's just that when the Acquire
line is executed, the code below it will definitely not be executed yet. After executing the Acquire
line, the code below it will have the opportunity to execute.
Is that right?