What does `Acquire` or `Release` alone mean in a `fetch_add`

Hi!

I'm currently reading Rust Atomics and Locks by Mara Bos and I'm a bit confused as to what a lone Release or Acquire mean for a fetch_add operation (and other fetch_X). My understanding is that the addition being atomic means that it will not get lost. Basically if several threads do a fetch_add adding values x1, x2, x3, .. xN then the only observable values are sums of subsets of {x1, x2, x3 ..., xN} and the final value is the some of them all. This conflicts with how I understand that either "the load is Acquire" or "the store is Released". If the operation is atomic, how can it be either without being AcqRel?

Hope someone can help clarify this for me,
Thanks

The ordering only matters if you are using unsafe code or more than one atomic.

The logic goes something like this: If an acquire load sees the value written by a release store, then, after the acquire load, you are guaranteed to see all writes (on stuff other than the atomic) that came before the release store.

2 Likes

Oh I see. So in fetch_X operations, the Ordering is only relevant in terms of thread synchronization.

Thanks for the very quick answer!

(I've marked alice's answer as approved but if anyone wants to add more intuition, please feel free to.)

Expanding on Alice's answer, the accesses to the atomic behave in exactly the same way regardless of ordering, and then the ordering parameter determines what impact this access has on things other than this atomic.

So, all accesses to the atomic mean that only one thread has access to the atomic for the duration of the access, and that all threads agree on the outcome of those accesses. This applies to all orderings. Orderings other than Relaxed define what happens with other accesses you've made from this thread.

Release adds the requirement that all stores by this thread before the Release store are visible to future Acquire loads. Acquire adds the requirement that all loads by this thread after the Acquire load will see stores made visible to the Acquire by a previous Release.

AcqRel is the combination of Acquire and Release for atomic operations that do both a load and a store (such as fetch_add, which does a load, an addition and then a store atomically).

SeqCst is the same as AcqRel, but also adds that for all SeqCst atomics in the program, all threads will agree on the order in which they were operated on. To actually be making use of this guarantee, you need to be reasoning about the behaviour of at least 3 threads (if you're only reasoning about one or two threads, it's the same as AcqRel) and at least 2 atomics (again, if you're only reasoning about one atomic, it's the same as AcqRel).

If all you care about is this atomic, use Relaxed. If that's not good enough, you should probably be using a higher-level synchronization object; if you're using other orderings, then you should be able to explain what it is that the extra requirements are doing for you.

For example, in another thread, Alice pointed out that the way an AtomicBool was being used was equivalent to it being a Mutex<(bool, ApplicationData)>, and thus the AcqRel ordering was needed to synchronize ApplicationData. Had the AtomicBool just been an optimized form of Mutex<bool>, then it would have been OK to use Relaxed.

If you're using orderings other than Relaxed (i.e. thinking about things other than just the atomic in front of your), I highly recommend buying, reading and understanding Rust Atomics and Locks - Mara has written an incredible guide to what all of this actually means, and thus what you need to understand in order to explain why you're using an ordering other than Relaxed.

1 Like

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.