mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-29 00:00:11 +02:00
threadpool: drop incomplete tasks on shutdown (#722)
## Motivation When the thread pool shuts down, futures that have been polled at least once but not completed yet are simply leaked. We should drop them instead. ## Solution Multiple changes are introduced: * Tasks are assigned a home worker the first time they are polled. * Each worker contains a set of tasks (`Arc<Task>`) it is home to. When a task is assigned a home worker, it is registered in that worker's set of tasks. When the task is completed, it is unregistered from the set. * When the thread pool shuts down and after all worker threads stop, the remaining tasks in workers' sets are aborted, i.e. they are switched to the `Aborted` state and their `Future`s are dropped. * The thread pool shutdown process is refactored to make it more robust. We don't track the number of active threads manually anymore. Instead, there's `Arc<ShutdownTrigger>` that aborts remaining tasks and completes the `Shutdown` future once it gets destroyed (when all `Worker`s and `ThreadPool` get dropped because they're the only ones to contain strong references to the `ShutdownTrigger`). Closes #424 Closes #428
This commit is contained in:
@@ -451,6 +451,13 @@ impl Worker {
|
||||
fn run_task(&self, task: Arc<Task>, notify: &Arc<Notifier>) {
|
||||
use task::Run::*;
|
||||
|
||||
// If this is the first time this task is being polled, register it so that we can keep
|
||||
// track of tasks that are in progress.
|
||||
if task.reg_worker.get().is_none() {
|
||||
task.reg_worker.set(Some(self.id.0 as u32));
|
||||
self.entry().register_task(&task);
|
||||
}
|
||||
|
||||
let run = self.run_task2(&task, notify);
|
||||
|
||||
// TODO: Try to claim back the worker state in case the backup thread
|
||||
@@ -497,6 +504,16 @@ impl Worker {
|
||||
}
|
||||
}
|
||||
|
||||
// Find which worker polled this task first.
|
||||
let worker = task.reg_worker.get().unwrap() as usize;
|
||||
|
||||
// Unregister the task from the worker it was registered in.
|
||||
if !self.is_blocking.get() && worker == self.id.0 {
|
||||
self.entry().unregister_task(task);
|
||||
} else {
|
||||
self.pool.workers[worker].remotely_complete_task(task);
|
||||
}
|
||||
|
||||
// The worker's run loop will detect the shutdown state
|
||||
// next iteration.
|
||||
return;
|
||||
@@ -672,11 +689,7 @@ impl Worker {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(*self.entry().park.get())
|
||||
.park()
|
||||
.unwrap();
|
||||
}
|
||||
self.entry().park();
|
||||
|
||||
trace!(" -> wakeup; idx={}", self.id.0);
|
||||
}
|
||||
@@ -690,11 +703,7 @@ impl Worker {
|
||||
fn sleep_light(&self) {
|
||||
const STEAL_COUNT: usize = 32;
|
||||
|
||||
unsafe {
|
||||
(*self.entry().park.get())
|
||||
.park_timeout(Duration::from_millis(0))
|
||||
.unwrap();
|
||||
}
|
||||
self.entry().park_timeout(Duration::from_millis(0));
|
||||
|
||||
for _ in 0..STEAL_COUNT {
|
||||
if let Some(task) = self.pool.queue.pop() {
|
||||
|
||||
Reference in New Issue
Block a user