Skip to content

Commit 3ba7f46

Browse files
committed
Move panic payload state from Machine to Thread
1 parent 9123f0e commit 3ba7f46

File tree

5 files changed

+124
-13
lines changed

5 files changed

+124
-13
lines changed

src/machine.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,6 @@ pub struct Evaluator<'mir, 'tcx> {
252252
pub(crate) file_handler: shims::posix::FileHandler,
253253
pub(crate) dir_handler: shims::posix::DirHandler,
254254

255-
/// The temporary used for storing the argument of
256-
/// the call to `miri_start_panic` (the panic payload) when unwinding.
257-
/// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`.
258-
pub(crate) panic_payload: Option<Scalar<Tag>>,
259-
260255
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
261256
pub(crate) time_anchor: Instant,
262257

@@ -291,7 +286,6 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
291286
validate,
292287
file_handler: Default::default(),
293288
dir_handler: Default::default(),
294-
panic_payload: None,
295289
time_anchor: Instant::now(),
296290
layouts,
297291
threads: ThreadManager::default(),

src/shims/panic.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4848
// Get the raw pointer stored in arg[0] (the panic payload).
4949
let &[payload] = check_arg_count(args)?;
5050
let payload = this.read_scalar(payload)?.check_init()?;
51-
assert!(
52-
this.machine.panic_payload.is_none(),
53-
"the panic runtime should avoid double-panics"
54-
);
55-
this.machine.panic_payload = Some(payload);
51+
this.set_panic_payload(payload);
5652

5753
// Jump to the unwind block to begin unwinding.
5854
this.unwind_to_block(unwind);
@@ -132,9 +128,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
132128
// We set the return value of `try` to 1, since there was a panic.
133129
this.write_scalar(Scalar::from_i32(1), catch_unwind.dest)?;
134130

135-
// `panic_payload` holds what was passed to `miri_start_panic`.
131+
// The Thread's `panic_payload` holds what was passed to `miri_start_panic`.
136132
// This is exactly the second argument we need to pass to `catch_fn`.
137-
let payload = this.machine.panic_payload.take().unwrap();
133+
let payload = this.take_panic_payload();
138134

139135
// Push the `catch_fn` stackframe.
140136
let f_instance = this.memory.get_fn(catch_unwind.catch_fn)?.as_instance()?;

src/thread.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,21 @@ enum ThreadJoinStatus {
106106
/// A thread.
107107
pub struct Thread<'mir, 'tcx> {
108108
state: ThreadState,
109+
109110
/// Name of the thread.
110111
thread_name: Option<Vec<u8>>,
112+
111113
/// The virtual call stack.
112114
stack: Vec<Frame<'mir, 'tcx, Tag, FrameData<'tcx>>>,
115+
113116
/// The join status.
114117
join_status: ThreadJoinStatus,
118+
119+
/// The temporary used for storing the argument of
120+
/// the call to `miri_start_panic` (the panic payload) when unwinding.
121+
/// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`.
122+
panic_payload: Option<Scalar<Tag>>,
123+
115124
}
116125

117126
impl<'mir, 'tcx> Thread<'mir, 'tcx> {
@@ -150,6 +159,7 @@ impl<'mir, 'tcx> Default for Thread<'mir, 'tcx> {
150159
thread_name: None,
151160
stack: Vec::new(),
152161
join_status: ThreadJoinStatus::Joinable,
162+
panic_payload: None,
153163
}
154164
}
155165
}
@@ -509,6 +519,21 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
509519
throw_machine_stop!(TerminationInfo::Deadlock);
510520
}
511521
}
522+
523+
/// Store the panic payload when beginning unwinding.
524+
fn set_panic_payload(&mut self, payload: Scalar<Tag>) {
525+
let thread = self.active_thread_mut();
526+
assert!(
527+
thread.panic_payload.is_none(),
528+
"the panic runtime should avoid double-panics"
529+
);
530+
thread.panic_payload = Some(payload);
531+
}
532+
533+
/// Retrieve the panic payload, for use in `catch_unwind`.
534+
fn take_panic_payload(&mut self) -> Scalar<Tag> {
535+
self.active_thread_mut().panic_payload.take().unwrap()
536+
}
512537
}
513538

514539
// Public interface to thread management.
@@ -686,4 +711,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
686711
}
687712
Ok(())
688713
}
714+
715+
/// Store the panic payload when beginning unwinding.
716+
fn set_panic_payload(&mut self, payload: Scalar<Tag>) {
717+
let this = self.eval_context_mut();
718+
this.machine.threads.set_panic_payload(payload);
719+
}
720+
721+
/// Retrieve the panic payload, for use in `catch_unwind`.
722+
fn take_panic_payload(&mut self) -> Scalar<Tag> {
723+
let this = self.eval_context_mut();
724+
this.machine.threads.take_panic_payload()
725+
}
689726
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// ignore-windows: Concurrency on Windows is not supported yet.
2+
use std::sync::{Arc, Condvar, Mutex};
3+
use std::thread::{spawn, JoinHandle};
4+
5+
struct BlockOnDrop(Option<JoinHandle<()>>);
6+
7+
impl BlockOnDrop {
8+
fn new(handle: JoinHandle<()>) -> BlockOnDrop {
9+
BlockOnDrop(Some(handle))
10+
}
11+
}
12+
13+
impl Drop for BlockOnDrop {
14+
fn drop(&mut self) {
15+
let _ = self.0.take().unwrap().join();
16+
}
17+
}
18+
19+
/// Cause a panic in one thread while another thread is unwinding.
20+
fn main() {
21+
let t1_started_pair = Arc::new((Mutex::new(false), Condvar::new()));
22+
let t2_started_pair = Arc::new((Mutex::new(false), Condvar::new()));
23+
24+
let t1_continue_mutex = Arc::new(Mutex::new(()));
25+
let t1_continue_guard = t1_continue_mutex.lock();
26+
27+
let t1 = {
28+
let t1_started_pair = t1_started_pair.clone();
29+
let t1_continue_mutex = t1_continue_mutex.clone();
30+
spawn(move || {
31+
let (mutex, condvar) = &*t1_started_pair;
32+
*mutex.lock().unwrap() = true;
33+
condvar.notify_one();
34+
35+
drop(t1_continue_mutex.lock());
36+
panic!("panic in thread 1");
37+
})
38+
};
39+
let t2 = {
40+
let t2_started_pair = t2_started_pair.clone();
41+
let block_on_drop = BlockOnDrop::new(t1);
42+
spawn(move || {
43+
let _ = block_on_drop;
44+
45+
let (mutex, condvar) = &*t2_started_pair;
46+
*mutex.lock().unwrap() = true;
47+
condvar.notify_one();
48+
49+
panic!("panic in thread 2");
50+
})
51+
};
52+
53+
// Wait for thread 1 to signal it has started.
54+
let (t1_started_mutex, t1_started_condvar) = &*t1_started_pair;
55+
let mut t1_started_guard = t1_started_mutex.lock().unwrap();
56+
while !*t1_started_guard {
57+
t1_started_guard = t1_started_condvar.wait(t1_started_guard).unwrap();
58+
}
59+
// Thread 1 should now be blocked waiting on t1_continue_mutex.
60+
61+
// Wait for thread 2 to signal it has started.
62+
let (t2_started_mutex, t2_started_condvar) = &*t2_started_pair;
63+
let mut t2_started_guard = t2_started_mutex.lock().unwrap();
64+
while !*t2_started_guard {
65+
t2_started_guard = t2_started_condvar.wait(t2_started_guard).unwrap();
66+
}
67+
// Thread 2 should now have already panicked and be in the middle of
68+
// unwinding. It should now be blocked on joining thread 1.
69+
70+
// Unlock t1_continue_mutex, and allow thread 1 to proceed.
71+
drop(t1_continue_guard);
72+
// Thread 1 will panic the next time it is scheduled. This will test the
73+
// behavior of interest to this test, whether Miri properly handles
74+
// concurrent panics in two different threads.
75+
76+
// Block the main thread on waiting to join thread 2. Thread 2 should
77+
// already be blocked on joining thread 1, so thread 1 will be scheduled
78+
// to run next, as it is the only ready thread.
79+
assert!(t2.join().is_err());
80+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
warning: thread support is experimental. For example, Miri does not detect data races yet.
2+
3+
thread '<unnamed>' panicked at 'panic in thread 2', $DIR/concurrent-panic.rs:49:13
4+
thread '<unnamed>' panicked at 'panic in thread 1', $DIR/concurrent-panic.rs:36:13

0 commit comments

Comments
 (0)