// 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>); 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"); }