Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit ec003fd

Browse files
committed
yield the main thread a number of times after its TLS dtors are done
1 parent 0849084 commit ec003fd

File tree

1 file changed

+37
-14
lines changed

1 file changed

+37
-14
lines changed

src/tools/miri/src/eval.rs

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ use rustc_session::config::EntryFnType;
2424
use crate::shims::tls;
2525
use crate::*;
2626

27+
/// When the main thread would exit, we will yield to any other thread that is ready to execute.
28+
/// But we must only do that a finite number of times, or a background thread running `loop {}`
29+
/// will hang the program.
30+
const MAIN_THREAD_YIELDS_AT_SHUTDOWN: u32 = 1_000;
31+
2732
#[derive(Copy, Clone, Debug, PartialEq)]
2833
pub enum AlignmentCheck {
2934
/// Do not check alignment.
@@ -180,6 +185,10 @@ enum MainThreadState {
180185
#[default]
181186
Running,
182187
TlsDtors(tls::TlsDtorsState),
188+
Yield {
189+
remaining: u32,
190+
},
191+
Done,
183192
}
184193

185194
impl MainThreadState {
@@ -196,22 +205,36 @@ impl MainThreadState {
196205
match state.on_stack_empty(this)? {
197206
Poll::Pending => {} // just keep going
198207
Poll::Ready(()) => {
199-
// Need to call `thread_terminated` ourselves since we are not going to
200-
// return to the scheduler loop.
201-
this.thread_terminated()?;
202-
// Raise exception to stop program execution.
203-
let ret_place = MPlaceTy::from_aligned_ptr(
204-
this.machine.main_fn_ret_place.unwrap().ptr,
205-
this.machine.layouts.isize,
206-
);
207-
let exit_code =
208-
this.read_scalar(&ret_place.into())?.to_machine_isize(this)?;
209-
throw_machine_stop!(TerminationInfo::Exit {
210-
code: exit_code,
211-
leak_check: true
212-
});
208+
// Give background threads a chance to finish by yielding the main thread a
209+
// couple of times -- but only if we would also preempt threads randomly.
210+
if this.machine.preemption_rate > 0.0 {
211+
*self = Yield { remaining: MAIN_THREAD_YIELDS_AT_SHUTDOWN };
212+
} else {
213+
*self = Done;
214+
}
213215
}
214216
},
217+
Yield { remaining } =>
218+
match remaining.checked_sub(1) {
219+
None => *self = Done,
220+
Some(new_remaining) => {
221+
*remaining = new_remaining;
222+
this.yield_active_thread();
223+
}
224+
},
225+
Done => {
226+
// Figure out exit code.
227+
let ret_place = MPlaceTy::from_aligned_ptr(
228+
this.machine.main_fn_ret_place.unwrap().ptr,
229+
this.machine.layouts.isize,
230+
);
231+
let exit_code = this.read_scalar(&ret_place.into())?.to_machine_isize(this)?;
232+
// Need to call `thread_terminated` ourselves since we are not going to
233+
// return to the scheduler loop.
234+
this.thread_terminated()?;
235+
// Stop interpreter loop.
236+
throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
237+
}
215238
}
216239
Ok(Poll::Pending)
217240
}

0 commit comments

Comments
 (0)