oden/third-party/vendor/loom/tests/spec.rs
2024-03-08 11:03:01 -08:00

163 lines
5.3 KiB
Rust

//! These tests are converted from the [C11 memory ordering page][spec].
//!
//!
//! [spec]: https://en.cppreference.com/w/cpp/atomic/memory_order
/// https://en.cppreference.com/w/cpp/atomic/memory_order#Relaxed_ordering
///
/// This test is ignored because loom cannot fully model `Ordering::Relaxed`.
#[test]
#[should_panic]
#[ignore]
fn relaxed() {
use loom::sync::atomic::AtomicUsize;
use loom::thread;
use std::sync::atomic::Ordering::Relaxed;
loom::model(|| {
let x1: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
let x2 = x1;
let y1: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
let y2 = y1;
let t1 = thread::spawn(move || {
let r1 = y1.load(Relaxed);
x1.store(r1, Relaxed);
r1
});
let t2 = thread::spawn(move || {
let r2 = x2.load(Relaxed);
y2.store(42, Relaxed);
r2
});
let r1 = t1.join().unwrap();
let r2 = t2.join().unwrap();
if r1 == 42 && r2 == 42 {
panic!("This case is possible with Relaxed, so we should hit this panic.");
}
});
}
/// https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering
///
/// This is the SeqCst example modified to use AcqRel to see that we indeed exercise all the
/// possible executions.
#[test]
fn acq_rel() {
use loom::sync::atomic::AtomicBool;
use loom::thread;
use std::sync::atomic::Ordering;
let mut builder = loom::model::Builder::new();
// The yield loop makes loom really sad without this:
builder.preemption_bound = Some(1);
let seen: &'static _ = Box::leak(Box::new(std::sync::Mutex::new(
std::collections::HashSet::new(),
)));
builder.check(move || {
let x: &'static _ = Box::leak(Box::new(AtomicBool::new(false)));
let y: &'static _ = Box::leak(Box::new(AtomicBool::new(false)));
let z: &'static _ = Box::leak(Box::new(std::sync::atomic::AtomicUsize::new(0)));
// NOTE: done in this thread after spawning
// thread::spawn(move || {
// x.store(true, Ordering::Release);
// });
thread::spawn(move || {
y.store(true, Ordering::Release);
});
let t1 = thread::spawn(move || {
while !x.load(Ordering::Acquire) {
loom::thread::yield_now();
}
if y.load(Ordering::Acquire) {
z.fetch_add(1, Ordering::Relaxed);
}
});
let t2 = thread::spawn(move || {
while !y.load(Ordering::Acquire) {
loom::thread::yield_now();
}
if x.load(Ordering::Acquire) {
z.fetch_add(1, Ordering::Relaxed);
}
});
x.store(true, Ordering::Release);
t1.join().unwrap();
t2.join().unwrap();
// Read z but not while holding the lock, since the read goes into loom innards.
let z = z.load(Ordering::SeqCst);
seen.lock().unwrap().insert(z);
});
let seen = seen.lock().unwrap();
assert!(seen.contains(&0));
assert!(seen.contains(&1));
assert!(seen.contains(&2));
assert_eq!(seen.len(), 3);
}
/// https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering
///
/// This test currently fails because loom executes a permutation that isn't legal under `SeqCst`
/// according to the spec in which `z == 0`.
#[test]
#[ignore]
fn test_seq_cst() {
use loom::sync::atomic::AtomicBool;
use loom::thread;
use std::sync::atomic::Ordering;
let mut builder = loom::model::Builder::new();
// The yield loop makes loom really sad without this:
builder.preemption_bound = Some(1);
let seen: &'static _ = Box::leak(Box::new(std::sync::Mutex::new(
std::collections::HashSet::new(),
)));
builder.check(move || {
let x: &'static _ = Box::leak(Box::new(AtomicBool::new(false)));
let y: &'static _ = Box::leak(Box::new(AtomicBool::new(false)));
let z: &'static _ = Box::leak(Box::new(std::sync::atomic::AtomicUsize::new(0)));
// NOTE: done in this thread after spawning
// thread::spawn(move || {
// x.store(true, Ordering::SeqCst);
// });
thread::spawn(move || {
y.store(true, Ordering::SeqCst);
});
let t1 = thread::spawn(move || {
while !x.load(Ordering::SeqCst) {
loom::thread::yield_now();
}
if y.load(Ordering::SeqCst) {
z.fetch_add(1, Ordering::Relaxed);
}
});
let t2 = thread::spawn(move || {
while !y.load(Ordering::SeqCst) {
loom::thread::yield_now();
}
if x.load(Ordering::SeqCst) {
z.fetch_add(1, Ordering::Relaxed);
}
});
x.store(true, Ordering::SeqCst);
t1.join().unwrap();
t2.join().unwrap();
// Read z but not while holding the lock, since the read goes into loom innards.
let z = z.load(Ordering::SeqCst);
assert_ne!(z, 0, "z == 0 is not possible with SeqCst");
seen.lock().unwrap().insert(z);
});
let seen = seen.lock().unwrap();
assert!(seen.contains(&1));
assert!(seen.contains(&2));
assert_eq!(seen.len(), 2);
}