//@ignore-target-windows: Concurrency on Windows is not supported yet. //@compile-flags: -Zmiri-ignore-leaks -Zmiri-preemption-rate=0 // Tests showing weak memory behaviours are exhibited. All tests // return true when the desired behaviour is seen. // This is scheduler and pseudo-RNG dependent, so each test is // run multiple times until one try returns true. // Spurious failure is possible, if you are really unlucky with // the RNG and always read the latest value from the store buffer. use std::sync::atomic::Ordering::*; use std::sync::atomic::{fence, AtomicUsize}; use std::thread::spawn; #[derive(Copy, Clone)] struct EvilSend(pub T); unsafe impl Send for EvilSend {} unsafe impl Sync for EvilSend {} // We can't create static items because we need to run each test // multiple times fn static_atomic(val: usize) -> &'static AtomicUsize { let ret = Box::leak(Box::new(AtomicUsize::new(val))); ret } // Spins until it reads the given value fn reads_value(loc: &AtomicUsize, val: usize) -> usize { while loc.load(Relaxed) != val { std::hint::spin_loop(); } val } fn relaxed() -> bool { let x = static_atomic(0); let j1 = spawn(move || { x.store(1, Relaxed); x.store(2, Relaxed); }); let j2 = spawn(move || x.load(Relaxed)); j1.join().unwrap(); let r2 = j2.join().unwrap(); r2 == 1 } // https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf Figure 8 fn seq_cst() -> bool { let x = static_atomic(0); let j1 = spawn(move || { x.store(1, Relaxed); }); let j2 = spawn(move || { x.store(2, SeqCst); x.store(3, SeqCst); }); let j3 = spawn(move || x.load(SeqCst)); j1.join().unwrap(); j2.join().unwrap(); let r3 = j3.join().unwrap(); r3 == 1 } fn initialization_write(add_fence: bool) -> bool { let x = static_atomic(11); assert_eq!(x.load(Relaxed), 11); // work around https://github.com/rust-lang/miri/issues/2164 let wait = static_atomic(0); let j1 = spawn(move || { x.store(22, Relaxed); // Relaxed is intentional. We want to test if the thread 2 reads the initialisation write // after a relaxed write wait.store(1, Relaxed); }); let j2 = spawn(move || { reads_value(wait, 1); if add_fence { fence(AcqRel); } x.load(Relaxed) }); j1.join().unwrap(); let r2 = j2.join().unwrap(); r2 == 11 } fn faa_replaced_by_load() -> bool { // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 #[no_mangle] pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize { storing.store(1, Relaxed); fence(Release); // sync.fetch_add(0, Relaxed); sync.load(Relaxed); fence(Acquire); loading.load(Relaxed) } let x = static_atomic(0); assert_eq!(x.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164 let y = static_atomic(0); assert_eq!(y.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164 let z = static_atomic(0); assert_eq!(z.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164 // Since each thread is so short, we need to make sure that they truely run at the same time // Otherwise t1 will finish before t2 even starts let go = static_atomic(0); let t1 = spawn(move || { while go.load(Relaxed) == 0 {} rdmw(y, x, z) }); let t2 = spawn(move || { while go.load(Relaxed) == 0 {} rdmw(z, x, y) }); go.store(1, Relaxed); let a = t1.join().unwrap(); let b = t2.join().unwrap(); (a, b) == (0, 0) } /// Asserts that the function returns true at least once in 100 runs fn assert_once(f: fn() -> bool) { assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x)); } pub fn main() { assert_once(relaxed); assert_once(seq_cst); assert_once(|| initialization_write(false)); assert_once(|| initialization_write(true)); assert_once(faa_replaced_by_load); }