rt: fix blocking pool shutdown logic (#1978)

The blocking task queue was not explicitly drained as part of the
blocking pool shutdown logic. It was originally assumed that the
contents of the queue would be dropped when the blocking pool structure
is dropped. However, tasks must be explicitly shutdown, so we must drain
the queue can call `shutdown` on each task.

Fixes #1970, #1946
This commit is contained in:
Carl Lerche
2019-12-17 21:24:26 -08:00
committed by GitHub
parent 17e424112d
commit 83cd754bc8
3 changed files with 45 additions and 1 deletions
+11 -1
View File
@@ -303,7 +303,9 @@ impl Inner {
break;
}
if timeout_result.timed_out() {
// Even if the condvar "timed out", if the pool is entering the
// shutdown phase, we want to perform the cleanup logic.
if !shared.shutdown && timeout_result.timed_out() {
break 'main;
}
@@ -311,6 +313,14 @@ impl Inner {
}
if shared.shutdown {
// Drain the queue
while let Some(task) = shared.queue.pop_front() {
drop(shared);
task.shutdown();
shared = self.shared.lock().unwrap();
}
// Work was produced, and we "took" it (by decrementing num_notify).
// This means that num_idle was decremented once for our wakeup.
// But, since we are exiting, we need to "undo" that, as we'll stay idle.
+31
View File
@@ -0,0 +1,31 @@
use crate::runtime::{self, Runtime};
use std::sync::Arc;
#[test]
fn blocking_shutdown() {
loom::model(|| {
let v = Arc::new(());
let rt = mk_runtime(1);
rt.enter(|| {
for _ in 0..2 {
let v = v.clone();
crate::task::spawn_blocking(move || {
assert!(1 < Arc::strong_count(&v));
});
}
});
drop(rt);
assert_eq!(1, Arc::strong_count(&v));
});
}
fn mk_runtime(num_threads: usize) -> Runtime {
runtime::Builder::new()
.threaded_scheduler()
.num_threads(num_threads)
.build()
.unwrap()
}
+3
View File
@@ -2,3 +2,6 @@
#[cfg(loom)]
pub(crate) mod loom_oneshot;
#[cfg(loom)]
pub(crate) mod loom_blocking;