

























April 17, 2026, 9:52am 1
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::thread;
fn main() {
let v = AtomicI32::new(0);
let flag = AtomicBool::new(false);
thread::scope(|s| {
s.spawn(|| {
while !flag.load(Ordering::Relaxed) { // #1
std::hint::spin_loop();
}
assert_eq!(v.swap(2, Ordering::Relaxed), 1); // #2
// assert_eq!(v.load(Ordering::Relaxed),1); // #5
});
s.spawn(|| {
if v.swap(1, Ordering::Relaxed) == 0 { // #3
flag.store(true, Ordering::Relaxed); // #4
}
});
});
}
In this example, the assert at #2 never fails. However, if you comment out #2 and uncomment #5, the assert at #5 can fail because #5 can either read 0 or 1.
Does it mean that an RMW is less prone to reading stale values than a pure load? So, for flag checking, we should use RMW operations rather than a pure load? If not that, what's the plausible reason here?
absolutely not. if you want ordering relations you should use the relevant atomic orderings and/or premade locks.
that being said, it is true that swap is less prone to reading "stale" values from a specific atomic in some sense. indeed the total modification order of v must be preserved, so in this specific case given #3 swapped v from 0 to 1, and #2 swapped v from something to 2, and v started at 0, it must necessarily be that what #2 swapped from 1.
in other words swap guarantees that if you only modify through swap, each value that is being swapped in will only be swapped out once.
you can think of it like if integers were !Copy, and swap was like core::mem::swap
mroth April 17, 2026, 2:59pm 3
You make wrong assumptions:
a.) The execution order of the two spawned threads is arbitrary. The second thread can start, execute and finish before the first thread even started.
b.) There is no stale data. There is only Ordering. By using Relaxed you state that there is no ordering between these two atomics. So they can be stored and fetched at any time the compiler or CPU thinks will be fine.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。