92 lines
3.2 KiB
Rust
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");
|
|
}
|