Skip to content

Yield from the main thread a few times at shutdown #2660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 25 additions & 22 deletions src/concurrency/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,31 +623,34 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
/// long as we can and switch only when we have to (the active thread was
/// blocked, terminated, or has explicitly asked to be preempted).
fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> {
// If we are the main thread, and our call stack is empty but our state is Enabled, we are
// about to terminate.
// But, if there are any other threads which can execute, yield to them instead of falling
// through to the termination state. This is to give them a chance to clean up and quit.
let active_thread = &self.threads[self.active_thread];
if self.active_thread == MAIN_THREAD
&& active_thread.should_terminate()
&& self.threads.iter().any(|t| t.state == ThreadState::Enabled && !t.stack.is_empty())
&& self.main_thread_yields_at_shutdown_remaining > 0
{
self.yield_active_thread = true;
self.main_thread_yields_at_shutdown_remaining -= 1;
} else {
// Check whether the thread ought to terminate, and if so start executing TLS
// destructors.
if self.threads[self.active_thread].should_terminate() {
self.threads[self.active_thread].state = ThreadState::Terminated;
return Ok(SchedulingAction::ExecuteDtors);
}
// Check whether the thread ought to terminate, and if so start executing TLS
// destructors.
if self.threads[self.active_thread].should_terminate() {
self.threads[self.active_thread].state = ThreadState::Terminated;
return Ok(SchedulingAction::ExecuteDtors);
}

// If we get here again and the thread is *still* terminated, there are no more dtors to run.
if self.threads[MAIN_THREAD].state == ThreadState::Terminated {
// The main thread terminated; stop the program.
// We do *not* run TLS dtors of remaining threads, which seems to match rustc behavior.
return Ok(SchedulingAction::Stop);
// If we are the main thread and we are Terminated, we ought to stop.
// But, if there are any other threads which can execute, yield to them instead of falling
// through to the termination state. This is to give them a chance to clean up and quit.
let active_thread = &self.threads[self.active_thread];
if self.active_thread == MAIN_THREAD
&& active_thread.state == ThreadState::Terminated
Comment on lines +639 to +640
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already know self.threads[MAIN_THREAD].state == ThreadState::Terminated here, so this seems redundant?

&& self
.threads
.iter()
.any(|t| t.state == ThreadState::Enabled && !t.stack.is_empty())
&& self.main_thread_yields_at_shutdown_remaining > 0
{
self.yield_active_thread = true;
self.main_thread_yields_at_shutdown_remaining -= 1;
} else {
// The main thread terminated; stop the program.
// We do *not* run TLS dtors of remaining threads, which seems to match rustc behavior.
return Ok(SchedulingAction::Stop);
}
}
// This thread and the program can keep going.
if self.threads[self.active_thread].state == ThreadState::Enabled
Expand Down