use loom::sync::Arc;
use loom::sync::atomic::{AtomicBool, Ordering as Seq};
use loom::thread;
use super::*;
struct Stop(Arc<AtomicBool>);
impl Drop for Stop {
fn drop(&mut self) {
assert!(!self.0.swap(true, Seq::SeqCst), "stopped twice");
}
}
fn program() -> (Stop, Arc<AtomicBool>) {
let stopped = Arc::new(AtomicBool::new(false));
(Stop(stopped.clone()), stopped)
}
#[test]
fn the_newest_command_runs_and_an_older_end_never_clears_it() {
loom::model(|| {
let slot = Arc::new(Slot::new());
let (a, a_stopped) = program();
let (b, b_stopped) = program();
let older = {
let slot = slot.clone();
thread::spawn(move || {
let generation = slot.start(a);
slot.end(generation);
})
};
slot.start(b);
older.join().unwrap();
assert!(a_stopped.load(Seq::SeqCst));
assert_eq!(slot.is_running(), !b_stopped.load(Seq::SeqCst));
});
}
#[test]
fn a_stop_racing_two_commands_leaves_at_most_one_running() {
loom::model(|| {
let slot = Arc::new(Slot::new());
let (a, a_stopped) = program();
let (b, b_stopped) = program();
let first = {
let slot = slot.clone();
thread::spawn(move || {
slot.start(a);
})
};
let stopper = {
let slot = slot.clone();
thread::spawn(move || {
slot.stop();
})
};
slot.start(b);
first.join().unwrap();
stopper.join().unwrap();
let alive = [&a_stopped, &b_stopped]
.iter()
.filter(|stopped| !stopped.load(Seq::SeqCst))
.count();
assert_eq!(alive, usize::from(slot.is_running()));
assert!(alive <= 1);
slot.stop();
assert!(a_stopped.load(Seq::SeqCst) && b_stopped.load(Seq::SeqCst));
});
}