diff --git a/src/bounded.rs b/src/bounded.rs index 7a7fb71..b98846c 100644 --- a/src/bounded.rs +++ b/src/bounded.rs @@ -129,8 +129,7 @@ impl Bounded { } } } else if stamp.wrapping_add(self.one_lap) == tail + 1 { - crate::full_fence(); - let head = self.head.load(Ordering::Relaxed); + let head = crate::full_fence_for_load(|| self.head.load(Ordering::Relaxed)); // If the head lags one lap behind the tail as well... if head.wrapping_add(self.one_lap) == tail { @@ -191,8 +190,7 @@ impl Bounded { } } } else if stamp == head { - crate::full_fence(); - let tail = self.tail.load(Ordering::Relaxed); + let tail = crate::full_fence_for_load(|| self.tail.load(Ordering::Relaxed)); // If the tail equals the head, that means the queue is empty. if (tail & !self.mark_bit) == head { diff --git a/src/lib.rs b/src/lib.rs index c4e5abd..3133e6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -445,8 +445,11 @@ impl fmt::Display for PushError { /// Equivalent to `atomic::fence(Ordering::SeqCst)`, but in some cases faster. #[inline] -fn full_fence() { - if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { +fn full_fence_for_load(load_op: impl FnOnce() -> T) -> T { + if cfg!(all( + any(target_arch = "x86", target_arch = "x86_64"), + not(miri) + )) { // HACK(stjepang): On x86 architectures there are two different ways of executing // a `SeqCst` fence. // @@ -461,7 +464,11 @@ fn full_fence() { // x86 platforms is going to optimize this away. let a = AtomicUsize::new(0); let _ = a.compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst); + // On x86, `lock cmpxchg; mov` is fine. See also https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html. + load_op() } else { + let res = load_op(); atomic::fence(Ordering::SeqCst); + res } } diff --git a/src/unbounded.rs b/src/unbounded.rs index c7cba68..df1b674 100644 --- a/src/unbounded.rs +++ b/src/unbounded.rs @@ -237,8 +237,7 @@ impl Unbounded { let mut new_head = head + (1 << SHIFT); if new_head & MARK_BIT == 0 { - crate::full_fence(); - let tail = self.tail.index.load(Ordering::Relaxed); + let tail = crate::full_fence_for_load(|| self.tail.index.load(Ordering::Relaxed)); // If the tail equals the head, that means the queue is empty. if head >> SHIFT == tail >> SHIFT {