rt: avoid dropping a task in calls to wake() (#1972)

Calls to tasks should not be nested. Currently, while a task is being
executed and the runtime is shutting down, a call to wake() can result
in the wake target to be dropped. This, in turn, results in the drop
handler being called.

If the user holds a ref cell borrow, a mutex guard, or any such value,
dropping the task inline can result in a deadlock.

The fix is to permit tasks to be scheduled during the shutdown process
and dropping the tasks once they are popped from the queue.

Fixes #1929, #1886
This commit is contained in:
Carl Lerche
2019-12-17 20:52:09 -08:00
committed by GitHub
parent 8add90210b
commit 41d15ea212
5 changed files with 141 additions and 26 deletions
+36
View File
@@ -27,6 +27,42 @@ fn spawned_task_does_not_progress_without_block_on() {
assert_eq!(out, "hello");
}
#[test]
fn acquire_mutex_in_drop() {
use futures::future::pending;
use tokio::task;
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
let mut rt = rt();
rt.spawn(async move {
let _ = rx2.await;
unreachable!();
});
rt.spawn(async move {
let _ = rx1.await;
let _ = tx2.send(()).unwrap();
unreachable!();
});
// Spawn a task that will never notify
rt.spawn(async move {
pending::<()>().await;
tx1.send(()).unwrap();
});
// Tick the loop
rt.block_on(async {
task::yield_now().await;
});
// Drop the rt
drop(rt);
}
fn rt() -> Runtime {
tokio::runtime::Builder::new()
.basic_scheduler()