Atomic concurrency: when do I need something stronger than Ordering::Relaxed?

⚓ Rust    📅 2026-07-01    👤 surdeus    👁️ 2      

surdeus

Hi,

I am building some concurrent Rust code and trying to optimize a few small pieces of shared state using atomics such as AtomicBool.

One thing I am still struggling to understand is when Rust code actually needs memory orderings stronger than Ordering::Relaxed.

In C++, I can understand why this matters more directly. But in Rust, we have the borrow checker, and if ordinary memory is being mutated in one thread, another thread cannot safely read the same memory at the same time without some synchronization primitive.

Usually, this synchronization is done through Mutex, RwLock, channels, etc. My understanding is that those primitives already provide the necessary memory ordering through lock/unlock behavior, so adding stronger ordering to a separate atomic flag can often be redundant.

So my question is:

Can someone show a realistic Rust code example where using Ordering::Relaxed would be incorrect, and where Acquire/Release or AcqRel is actually necessary?

I am especially interested in examples where the code is still valid Rust, but the memory ordering is wrong and can lead to race conditions or other concurrency bugs.

One additional thing I realized while thinking about this: Arc::clone can be called concurrently from multiple threads. If I use Ordering::Relaxed on my own atomic flag near Arc::clone, could reordering around Arc::clone cause a race condition? My current guess is “probably not,” because Arc handles its own internal reference-counting correctly, and to mutate internal state, I would still need to go through Mutex, RwLock, or another synchronization primitive. But I would like to understand this better.

Thanks!

1 post - 1 participant

Read full topic

🏷️ Rust_feed