Atomic ordering and memory fence

Hi, I am a little bit confused about atomic ordering and memory fence.

For example, if I have some code like this, does the bar method lock-free when concurrent?


struct Foo {
     ctr: AtomicUsize,
     data: [u64; 10],
}

impl Foo {
      fn bar(&self, index: usize, val: u64) {
            let ctr = self.ctr.load(Ordering::Acquire);
            self.data[index] = val;
            self.ctr.store(ctr + 1, Ordering::Release);
      }
}

Although it doesn't solve your problem directly, the following video by Jon Gjengset is really good and useful for this topic: Crust of Rust: Atomics and Memory Ordering - YouTube

1 Like

Yes. Atomics don't lock, that's the whole point of them. Per the documentation:

All atomic types in this module are guaranteed to be lock-free if they’re available. This means they don’t internally acquire a global mutex.

1 Like

Beware, your code is probably not doing what you intended it to do.

Acquire and Release pertain to guarantees about re-ordering with respect to the following and the preceding reads/writes. Nothing guarantees that another thread doesn't jump in and do its own write in the meantime. For instance, if you compile this code on x86/x64, it will emit regular movs, which already have acq/rel semantics. But they still don't guarantee atomicity – you can trivially have a race condition. FWIW, using SeqCst wouldn't help here, either, for the same reason.

The following code (Playground) demonstrates this problem by triggering an assertion failure:

fn racy() {
    for _ in 0..1_000_000 {
        let x1 = X.load(SeqCst);
        let x2 = X.load(SeqCst);
        assert_eq!(x1, x2);
        X.store(x2 + 1, SeqCst);
    }
}

fn main() {
    let t1 = thread::spawn(racy);
    let t2 = thread::spawn(racy);
    println!("{:?}", t1.join());
    println!("{:?}", t2.join());
}
2 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.