| // We are making scheduler assumptions here. |
| //@compile-flags: -Zmiri-deterministic-concurrency |
| |
| //! 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::{JoinHandle, spawn}; |
| |
| 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 _capture = 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"); |
| } |