Rust atomics and locks -- Relaxed memory ordering

This text along with code is taken from Mara Bos's book which is an excellent book for learning low-level concurrency in Rust.

While atomic operations using relaxed memory ordering do not provide any
happens-before relationship, they do guarantee a total modification order of each individual atomic variable. This means that all modifications of the same atomic variable happen in an order that is the same from the perspective of every single thread.
To demonstrate what that means, consider the following example where we assume a and b are concurrently executed by different threads:

static X: AtomicI32 = AtomicI32::new(0);
fn a() {
X.fetch_add(5, Relaxed);
X.fetch_add(10, Relaxed);
}
fn b() {
let a = X.load(Relaxed);
let b = X.load(Relaxed);
let c = X.load(Relaxed);
let d = X.load(Relaxed);
println!("{a} {b} {c} {d}");
}

In this example, only one thread modifies X , which makes it easy to see that there’s only one possible order of modification of X : 0→5→15. It starts at zero, then becomes five, and is finally changed to fifteen. Threads cannot observe any values from X that are inconsistent with this total modification order. This means that 0 0 0 0 , 0 0 5 15 , and 0 15 15 15 are some of the possible results from the print statement in the other thread, while an output of 0 5 0 15 or 0 0 10 15 is impossible.

why output like 0 0 10 15 is impossible?

I bolded the text in two places. Later bolded text says that only one thread modifies the X, then I think I understood why output like 0 0 10 15 is impossible. However, if the compiler reorders the instruction like adding the 10 first to X, then I think output like 0 0 10 15 is possible. Is the compiler's instruction reordering possible here?

However, according to the first bolded text, it says both functions are concurrently executed by different threads. Here I think an output like 0 0 10 15 is possible.

My reasoning:
Assume many threads are running the function a(). A thread named T1 just executed the first instruction of the function which is X.fetch_add(5, Relaxed) so now the X is 5. Before going into the last instruction a thread called T2 also executed the first instruction of the a(), so now X became 10. Hence X with a value of 10 is possible.

What am I missing? Do you think I am bad at English comprehension?

The text doesn’t make it totally explicit, but the example is talking about a program that calls a() one time and b() one time, on separate threads.

You are correct that if a() were called more than once, other sequences would become possible.

2 Likes

The compiler is not allowed to make this kind of reordering.

1 Like

Then we would be majorly screwed. If side effects didn't keep their order, then you could rely on nothing, basically nothing ever would work. That sort of reordering isn't even allowed without atomics.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.