From 524e66314faacd9803792ce2a9dc13befd2ceeeb Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 27 Nov 2019 14:24:44 -0800 Subject: [PATCH] task: fix panic when dropping `LocalSet` (#1843) It turns out that the `Scheduler::release` method on `LocalSet`'s `Scheduler` *is* called, when the `Scheduler` is dropped with tasks still running. Currently, that method is `unreachable!`, which means that dropping a `LocalSet` with tasks running will panic. This commit fixes the panic, by pushing released tasks to `pending_drop`. This is the same as `BasicScheduler`. Fixes #1842 --- tokio/src/task/local.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tokio/src/task/local.rs b/tokio/src/task/local.rs index cb62bd5ed..6d0adf31c 100644 --- a/tokio/src/task/local.rs +++ b/tokio/src/task/local.rs @@ -345,8 +345,9 @@ impl Schedule for Scheduler { } } - fn release(&self, _: Task) { - unreachable!("tasks should only be completed locally") + fn release(&self, task: Task) { + // This will be called when dropping the local runtime. + self.pending_drop.push(task); } fn release_local(&self, task: &Task) { @@ -724,4 +725,32 @@ mod tests { join2.await.unwrap() }); } + #[test] + fn drop_cancels_tasks() { + // This test reproduces issue #1842 + use crate::sync::oneshot; + use std::time::Duration; + + let mut rt = runtime::Builder::new() + .enable_time() + .basic_scheduler() + .build() + .unwrap(); + + let (started_tx, started_rx) = oneshot::channel(); + + let local = LocalSet::new(); + local.spawn_local(async move { + started_tx.send(()).unwrap(); + loop { + crate::time::delay_for(Duration::from_secs(3600)).await; + } + }); + + local.block_on(&mut rt, async { + started_rx.await.unwrap(); + }); + drop(local); + drop(rt); + } }