rust/tests/run-pass/panic/concurrent-panic.rs
2020-08-31 19:32:14 -05:00

92 lines
3.2 KiB
Rust

// ignore-windows: Concurrency on Windows is not supported yet.
//! Cause a panic in one thread while another thread is unwinding. This checks
//! that separate threads have their own panicking state.
use std::sync::{Arc, Condvar, Mutex};
use std::thread::{spawn, JoinHandle};
struct BlockOnDrop(Option<JoinHandle<()>>);
impl BlockOnDrop {
fn new(handle: JoinHandle<()>) -> BlockOnDrop {
BlockOnDrop(Some(handle))
}
}
impl Drop for BlockOnDrop {
fn drop(&mut self) {
eprintln!("Thread 2 blocking on thread 1");
let _ = self.0.take().unwrap().join();
eprintln!("Thread 1 has exited");
}
}
fn main() {
let t1_started_pair = Arc::new((Mutex::new(false), Condvar::new()));
let t2_started_pair = Arc::new((Mutex::new(false), Condvar::new()));
let t1_continue_mutex = Arc::new(Mutex::new(()));
let t1_continue_guard = t1_continue_mutex.lock();
let t1 = {
let t1_started_pair = t1_started_pair.clone();
let t1_continue_mutex = t1_continue_mutex.clone();
spawn(move || {
eprintln!("Thread 1 starting, will block on mutex");
let (mutex, condvar) = &*t1_started_pair;
*mutex.lock().unwrap() = true;
condvar.notify_one();
drop(t1_continue_mutex.lock());
panic!("panic in thread 1");
})
};
// Wait for thread 1 to signal it has started.
let (t1_started_mutex, t1_started_condvar) = &*t1_started_pair;
let mut t1_started_guard = t1_started_mutex.lock().unwrap();
while !*t1_started_guard {
t1_started_guard = t1_started_condvar.wait(t1_started_guard).unwrap();
}
eprintln!("Thread 1 reported it has started");
// Thread 1 should now be blocked waiting on t1_continue_mutex.
let t2 = {
let t2_started_pair = t2_started_pair.clone();
let block_on_drop = BlockOnDrop::new(t1);
spawn(move || {
let _ = block_on_drop;
let (mutex, condvar) = &*t2_started_pair;
*mutex.lock().unwrap() = true;
condvar.notify_one();
panic!("panic in thread 2");
})
};
// Wait for thread 2 to signal it has started.
let (t2_started_mutex, t2_started_condvar) = &*t2_started_pair;
let mut t2_started_guard = t2_started_mutex.lock().unwrap();
while !*t2_started_guard {
t2_started_guard = t2_started_condvar.wait(t2_started_guard).unwrap();
}
eprintln!("Thread 2 reported it has started");
// Thread 2 should now have already panicked and be in the middle of
// unwinding. It should now be blocked on joining thread 1.
// Unlock t1_continue_mutex, and allow thread 1 to proceed.
eprintln!("Unlocking mutex");
drop(t1_continue_guard);
// Thread 1 will panic the next time it is scheduled. This will test the
// behavior of interest to this test, whether Miri properly handles
// concurrent panics in two different threads.
// Block the main thread on waiting to join thread 2. Thread 2 should
// already be blocked on joining thread 1, so thread 1 will be scheduled
// to run next, as it is the only ready thread.
assert!(t2.join().is_err());
eprintln!("Thread 2 has exited");
}