yield the main thread a number of times after its TLS dtors are done

This commit is contained in:
Ralf Jung 2022-11-27 12:30:50 +01:00
parent 0849084d06
commit ec003fdd9c

View File

@ -24,6 +24,11 @@
use crate::shims::tls;
use crate::*;
/// When the main thread would exit, we will yield to any other thread that is ready to execute.
/// But we must only do that a finite number of times, or a background thread running `loop {}`
/// will hang the program.
const MAIN_THREAD_YIELDS_AT_SHUTDOWN: u32 = 1_000;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AlignmentCheck {
/// Do not check alignment.
@ -180,6 +185,10 @@ enum MainThreadState {
#[default]
Running,
TlsDtors(tls::TlsDtorsState),
Yield {
remaining: u32,
},
Done,
}
impl MainThreadState {
@ -196,22 +205,36 @@ fn on_main_stack_empty<'tcx>(
match state.on_stack_empty(this)? {
Poll::Pending => {} // just keep going
Poll::Ready(()) => {
// Need to call `thread_terminated` ourselves since we are not going to
// return to the scheduler loop.
this.thread_terminated()?;
// Raise exception to stop program execution.
let ret_place = MPlaceTy::from_aligned_ptr(
this.machine.main_fn_ret_place.unwrap().ptr,
this.machine.layouts.isize,
);
let exit_code =
this.read_scalar(&ret_place.into())?.to_machine_isize(this)?;
throw_machine_stop!(TerminationInfo::Exit {
code: exit_code,
leak_check: true
});
// Give background threads a chance to finish by yielding the main thread a
// couple of times -- but only if we would also preempt threads randomly.
if this.machine.preemption_rate > 0.0 {
*self = Yield { remaining: MAIN_THREAD_YIELDS_AT_SHUTDOWN };
} else {
*self = Done;
}
}
},
Yield { remaining } =>
match remaining.checked_sub(1) {
None => *self = Done,
Some(new_remaining) => {
*remaining = new_remaining;
this.yield_active_thread();
}
},
Done => {
// Figure out exit code.
let ret_place = MPlaceTy::from_aligned_ptr(
this.machine.main_fn_ret_place.unwrap().ptr,
this.machine.layouts.isize,
);
let exit_code = this.read_scalar(&ret_place.into())?.to_machine_isize(this)?;
// Need to call `thread_terminated` ourselves since we are not going to
// return to the scheduler loop.
this.thread_terminated()?;
// Stop interpreter loop.
throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
}
}
Ok(Poll::Pending)
}