I see a code snippet in rust crossbeam-epoch library
pub(crate) fn pin(&self) -> Guard {
...
self.epoch.store(new_epoch, Ordering::Relaxed);
atomic::fence(Ordering::SeqCst);
...
I am curious that
- why not just
self.epoch.store(new_epoch, Ordering::SeqCst);? - Do they provide a same memory ordering guarantee?
- Is there any performance difference between these two approach?
(Maybe) Equivalent C++ code:
epoch.store(new_epoch, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
v.s.
epoch.store(new_epoch, std::memory_order_seq_cst);
They have different semantics for how they can be reordered with other loads and stores in this thread.
The
seq_cstfence acts as a full barrier; no loads or stores can be reordered across it. However, nothing here prevents the store toepochfrom being reordered with earlier loads or stores. (Provided that the ordering on those loads and stores also allows it; i.e. the store toepochcan be reordered with earlierrelaxedloads, and all earlier stores regardless of their specified ordering.)A
seq_cststore is not reordered with otherseq_cstloads and stores. However, for purposes of reordering with non-seq_cstaccesses, it is treated as if it were simplyrelease. It will not be reordered with earlier loads or stores, but nothing here prevents it from being reordered with later loads and stores. (Specifically,relaxedoracquireloads, orrelaxedstores.)To be concrete, consider the following code.
In
v1, the store toxcan be reordered with the store toa, but not with the store tob. Ifreaderis run in another thread, it may observeaa == false && xx == true, but it must not observexx == false && bb == true.In
v2, the reverse is true: the store toxcan be reordered with the store tob, but not with the store toa. Thereaderfunction must not observeaa == false && xx == true, but it may observexx == false && bb == true.