diff --git a/crossbeam-deque/src/lib.rs b/crossbeam-deque/src/lib.rs index c46dcdd01..13e888d42 100644 --- a/crossbeam-deque/src/lib.rs +++ b/crossbeam-deque/src/lib.rs @@ -208,11 +208,21 @@ impl Buffer { } /// Writes `value` into the specified `index`. + /// + /// Using this concurrently with another `read` or `write` is technically + /// speaking UB due to data races. We should be using relaxed accesses, but + /// that would cost too much performance. Hence, as a HACK, we use volatile + /// accesses instead. Experimental evidence shows that this works. unsafe fn write(&self, index: isize, value: T) { ptr::write_volatile(self.at(index), value) } /// Reads a value from the specified `index`. + /// + /// Using this concurrently with a `write` is technically speaking UB due to + /// data races. We should be using relaxed accesses, but that would cost + /// too much performance. Hence, as a HACK, we use volatile accesses + /// instead. Experimental evidence shows that this works. unsafe fn read(&self, index: isize) -> T { ptr::read_volatile(self.at(index)) } diff --git a/crossbeam-epoch/src/internal.rs b/crossbeam-epoch/src/internal.rs index 81047480b..b21f43634 100644 --- a/crossbeam-epoch/src/internal.rs +++ b/crossbeam-epoch/src/internal.rs @@ -361,12 +361,20 @@ impl Local { // instruction. // // Both instructions have the effect of a full barrier, but benchmarks have shown - // that the second one makes pinning faster in this particular case. + // that the second one makes pinning faster in this particular case. It is not + // clear that this is permitted by the C++ memory model (SC fences work very + // differently from SC accesses), but experimental evidence suggests that this + // works fine. Using inline assembly would be a viable (and correct) alternative, + // but alas, that is not possible on stable Rust. let current = Epoch::starting(); let previous = self .epoch .compare_and_swap(current, new_epoch, Ordering::SeqCst); debug_assert_eq!(current, previous, "participant was expected to be unpinned"); + // We add a compiler fence to make it less likely for LLVM to do something wrong + // here. Formally, this is not enough to get rid of data races; practically, + // it should go a long way. + atomic::compiler_fence(Ordering::SeqCst); } else { self.epoch.store(new_epoch, Ordering::Relaxed); atomic::fence(Ordering::SeqCst);