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:
Stjepan Glavina
2019-01-17 22:12:25 +01:00
committed by GitHub
parent c980837581
commit 4c8f274db9
9 changed files with 310 additions and 34 deletions
-8
View File
@@ -45,12 +45,6 @@ pub(crate) struct Pool {
// Stack tracking sleeping workers.
sleep_stack: CachePadded<worker::Stack>,
// Number of workers that haven't reached the final state of shutdown
//
// This is only used to know when to single `shutdown_task` once the
// shutdown process has completed.
pub num_workers: AtomicUsize,
// Worker state
//
// A worker is a thread that is processing the work queue and polling
@@ -122,7 +116,6 @@ impl Pool {
let ret = Pool {
state: CachePadded::new(AtomicUsize::new(State::new().into())),
sleep_stack: CachePadded::new(worker::Stack::new()),
num_workers: AtomicUsize::new(0),
workers,
queue,
trigger,
@@ -313,7 +306,6 @@ impl Pool {
}
let trigger = match self.trigger.upgrade() {
// The pool is shutting down.
None => {
// The pool is shutting down.
return;