use shuttle::scheduler::PctScheduler;
use shuttle::sync::Mutex;
use shuttle::{check_pct, check_random, thread, Config, MaxSteps, Runner};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
use test_log::test;
const TEST_LENGTH: usize = 20;
fn figure5() {
let lock = Arc::new(Mutex::new(0usize));
let lock_clone = Arc::clone(&lock);
thread::spawn(move || {
for _ in 0..TEST_LENGTH {
thread::sleep(Duration::from_millis(1));
}
*lock_clone.lock().unwrap() = 1;
});
let l = lock.lock().unwrap();
assert_ne!(*l, 1, "thread 1 ran to completion");
}
#[test]
fn figure5_random() {
check_random(figure5, 100);
}
#[test]
#[should_panic(expected = "thread 1 ran to completion")]
fn figure5_pct() {
let scheduler = PctScheduler::new(1, 20);
let runner = Runner::new(scheduler, Default::default());
runner.run(figure5);
}
#[test]
fn one_step() {
let scheduler = PctScheduler::new(2, 100);
let runner = Runner::new(scheduler, Default::default());
runner.run(|| {
thread::spawn(|| {});
});
}
fn yield_spin_loop(use_yield: bool) {
const NUM_THREADS: usize = 4;
let scheduler = PctScheduler::new(1, 100);
let mut config = Config::new();
config.max_steps = MaxSteps::FailAfter(50);
let runner = Runner::new(scheduler, config);
runner.run(move || {
let count = Arc::new(AtomicUsize::new(0usize));
let _thds = (0..NUM_THREADS)
.map(|_| {
let count = count.clone();
thread::spawn(move || {
count.fetch_add(1, Ordering::SeqCst);
})
})
.collect::<Vec<_>>();
while count.load(Ordering::SeqCst) < NUM_THREADS {
if use_yield {
thread::yield_now();
} else {
thread::sleep(Duration::from_millis(1));
}
}
});
}
#[test]
fn yield_spin_loop_fair() {
yield_spin_loop(true);
}
#[test]
#[should_panic(expected = "exceeded max_steps bound")]
fn yield_spin_loop_unfair() {
yield_spin_loop(false);
}
#[test]
#[should_panic(expected = "null dereference")]
fn figure1a_pct() {
const COUNT: usize = 5usize;
let scheduler = PctScheduler::new(1, 20);
let runner = Runner::new(scheduler, Default::default());
runner.run(|| {
let t1 = Arc::new(Mutex::new(None));
let t2 = Arc::clone(&t1);
thread::spawn(move || {
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
*t1.lock().unwrap() = Some(1);
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
});
thread::spawn(move || {
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
let _ = t2.lock().unwrap().expect("null dereference");
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
});
});
}
fn figure1b(num_threads: usize) {
assert!(num_threads >= 2);
let x1 = Arc::new(Mutex::new(Some(1)));
let x2 = Arc::clone(&x1);
for _ in 0..num_threads - 2 {
thread::spawn(|| {
for _ in 0..5 {
thread::sleep(Duration::from_millis(1));
}
});
}
thread::spawn(move || {
for _ in 0..5 {
thread::sleep(Duration::from_millis(1));
}
*x1.lock().unwrap() = None;
for _ in 0..4 {
thread::sleep(Duration::from_millis(1));
}
});
thread::spawn(move || {
for _ in 0..4 {
thread::sleep(Duration::from_millis(1));
}
let b = {
let b = x2.lock().unwrap().is_some();
b
};
if b {
let _ = x2.lock().unwrap().expect("null dereference");
}
for _ in 0..4 {
thread::sleep(Duration::from_millis(1));
}
});
}
#[test]
#[should_panic(expected = "null dereference")]
fn figure1b_pct() {
let scheduler = PctScheduler::new(2, 300);
let runner = Runner::new(scheduler, Default::default());
runner.run(|| {
figure1b(2);
});
}
#[test]
#[should_panic(expected = "null dereference")]
fn figure1b_pct_with_many_tasks() {
let scheduler = PctScheduler::new(2, 16_000);
let runner = Runner::new(scheduler, Default::default());
runner.run(|| {
figure1b(20);
});
}
#[test]
#[should_panic(expected = "deadlock")]
fn figure_1c() {
const COUNT: usize = 4usize;
let scheduler = PctScheduler::new(2, 400);
let runner = Runner::new(scheduler, Default::default());
runner.run(|| {
let a1 = Arc::new(Mutex::new(0));
let a2 = Arc::clone(&a1);
let b1 = Arc::new(Mutex::new(0));
let b2 = Arc::clone(&b1);
thread::spawn(move || {
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
let a = a1.lock().unwrap();
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
let b = b1.lock().unwrap();
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
assert_eq!(*a + *b, 0)
});
thread::spawn(move || {
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
let b = b2.lock().unwrap();
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
let a = a2.lock().unwrap();
for _ in 0..COUNT {
thread::sleep(Duration::from_millis(1));
}
assert_eq!(*a + *b, 0);
});
});
}
#[test]
#[should_panic(expected = "test closure did not exercise any concurrency")]
fn no_concurrency() {
check_pct(|| {}, 10, 2);
}