use super::{WakeFlag, WakeGate};
use crate::sync::{Arc, AtomicBool, AtomicU64, Ordering, model, thread};
#[test]
fn loom_flag_change_is_seen_or_wakes() {
model(|| {
let flag = Arc::new(WakeFlag::new());
let _ = flag.raise();
let change = Arc::new(AtomicU64::new(0));
let producer = {
let (flag, change) = (flag.clone(), change.clone());
thread::spawn(move || {
change.store(1, Ordering::Relaxed);
flag.raise()
})
};
let _ = flag.take();
let seen = change.load(Ordering::Relaxed);
let woke = producer.join().unwrap();
assert!(seen == 1 || woke, "a change neither seen nor woken for");
});
}
#[test]
fn loom_flag_park_keeps_the_change() {
model(|| {
let flag = Arc::new(WakeFlag::new());
flag.park();
let change = Arc::new(AtomicU64::new(0));
let producer = {
let (flag, change) = (flag.clone(), change.clone());
thread::spawn(move || {
change.store(1, Ordering::Relaxed);
flag.raise()
})
};
flag.park();
let _ = flag.take();
let seen = change.load(Ordering::Relaxed);
let woke = producer.join().unwrap();
assert!(seen == 1 || woke, "a parked change was lost");
});
}
#[test]
fn loom_flag_one_wake_per_raise() {
model(|| {
let flag = Arc::new(WakeFlag::new());
let other = {
let flag = flag.clone();
thread::spawn(move || flag.raise())
};
let mine = flag.raise();
let theirs = other.join().unwrap();
assert!(mine ^ theirs, "two producers must wake exactly once");
});
}
#[test]
fn loom_gate_reveal_race() {
model(|| {
let gate = Arc::new(WakeGate::new());
gate.hide();
let producer = {
let gate = gate.clone();
thread::spawn(move || gate.pass())
};
let owed = gate.show();
let routed = producer.join().unwrap();
assert!(routed != owed, "routed {routed}, owed {owed}");
});
}
#[test]
fn loom_gate_hide_race() {
model(|| {
let gate = Arc::new(WakeGate::new());
let producer = {
let gate = gate.clone();
thread::spawn(move || gate.pass())
};
gate.hide();
let routed = producer.join().unwrap();
let owed = gate.show();
assert!(routed != owed, "routed {routed}, owed {owed}");
});
}
#[test]
fn loom_draw_path_composes() {
model(|| {
let flag = Arc::new(WakeFlag::new());
let gate = Arc::new(WakeGate::new());
gate.hide();
let change = Arc::new(AtomicU64::new(0));
let producer = {
let (flag, gate, change) = (flag.clone(), gate.clone(), change.clone());
thread::spawn(move || {
change.store(1, Ordering::Relaxed);
flag.raise() && gate.pass()
})
};
let drew = if gate.show() {
let _ = flag.take();
Some(change.load(Ordering::Relaxed))
} else {
None
};
let routed = producer.join().unwrap();
assert!(
routed || drew == Some(1),
"neither routed nor drawn: drew {drew:?}"
);
});
}
#[test]
fn loom_flag_peek_orders_the_decision_before_a_raise() {
model(|| {
let flag = Arc::new(WakeFlag::new());
let queued = Arc::new(AtomicBool::new(true));
let producer = {
let (flag, queued) = (flag.clone(), queued.clone());
thread::spawn(move || {
flag.raise()
&& queued
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
})
};
queued.swap(false, Ordering::Relaxed);
let seen = flag.peek();
let requeued = producer.join().unwrap();
assert!(
seen || requeued,
"the frame saw no request and no other frame was queued"
);
});
}