tokio-rs / loom

Concurrency permutation testing tool for Rust.
MIT License
2.14k stars 111 forks source link

loom doesn't respect `std::thread::sleep()` #193

Open tabokie opened 3 years ago

tabokie commented 3 years ago

I use loom to test a customized condvar, in specific its wait_timeout method. To make sure the waiter times out instead of getting woken up, I use std::thread::sleep() to freeze the notifier for a while. But it turns out assert!(timed_out) always fails when using loom. Is it because loom doesn't respect std::thread::sleep()? Or loom inject spurious wakes into loom::sync::condvar?

The test:

use loom::sync::atomic::{AtomicUsize, Ordering};
use loom::sync::{Arc, Mutex};
use loom::thread;
#[test]
fn test_condvar_loom() {
    loom::model(|| {
        let short_timeout_millis = 1;

        let mu = Arc::new(Mutex::new(()));
        let condv = Arc::new(Condvar::new());
        let mut ts = vec![];
        let enter_ticket = Arc::new(AtomicUsize::new(0));

        for i in 0..2 {
            let (mu, condv, enter) = (
                mu.clone(),
                condv.clone(),
                enter_ticket.clone(),
            );
            let t = thread::spawn(move || {
                let guard = mu.lock().unwrap();
                enter.fetch_add(1, Ordering::Relaxed);
                let (_, timed_out) = condv.wait_timeout(
                    guard,
                    Duration::from_millis(short_timeout_millis),
                );
                assert_eq!(timed_out, true);
            });
            ts.push(t);
        }
        ts.push(thread::spawn(move || {
            while enter_ticket.load(Ordering::Relaxed) != 2 {
                thread::yield_now();
            }
            {
                let _guard = mu.lock().unwrap();
                std::thread::sleep(Duration::from_millis(short_timeout_millis * 5);
                condv.notify_all();
            }
        }));
        for t in ts {
            t.join().unwrap();
        }
    });
}