From 7abbab5495dd79fedc3068b4ecb37feeec274038 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Tue, 27 Aug 2024 20:51:03 +0200 Subject: [PATCH 1/5] JoinSet, clarify behaviour of blocking code under abort/drop I noticed some confusion when users interact with JoinSet. While task::spawn_blocking notes that blocking code can not be aborted that same warning is not present in the JoinSet. While this makes perfect sense when you know how async works in Rust it is difficult for users less experienced with the inner workings of async. Example: https://github.com/RustAudio/rodio/issues/606#issuecomment-2312040571 --- tokio/src/task/join_set.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tokio/src/task/join_set.rs b/tokio/src/task/join_set.rs index a9cd8f52d55..5e66875cc8e 100644 --- a/tokio/src/task/join_set.rs +++ b/tokio/src/task/join_set.rs @@ -23,7 +23,10 @@ use crate::util::IdleNotifiedSet; /// /// All of the tasks must have the same return type `T`. /// -/// When the `JoinSet` is dropped, all tasks in the `JoinSet` are immediately aborted. +/// When the `JoinSet` is dropped, all non-blocking tasks in the `JoinSet` are +/// immediately aborted. Tasks spawned with +/// [`spawn_blocking`](Self::spawn_blocking) will abort when they reach an +/// `await` point /// /// # Examples /// @@ -205,6 +208,8 @@ impl JoinSet { /// it in this `JoinSet`, returning an [`AbortHandle`] that can be /// used to remotely cancel the task. /// + /// Note that the task can only abort once it reaches an `await` point. + /// /// # Examples /// /// Spawn multiple blocking tasks and wait for them. @@ -251,6 +256,8 @@ impl JoinSet { /// provided runtime and store it in this `JoinSet`, returning an /// [`AbortHandle`] that can be used to remotely cancel the task. /// + /// Note that the task can only abort once it reaches an `await` point. + /// /// [`AbortHandle`]: crate::task::AbortHandle #[track_caller] pub fn spawn_blocking_on(&mut self, f: F, handle: &Handle) -> AbortHandle @@ -367,6 +374,10 @@ impl JoinSet { /// This method ignores any panics in the tasks shutting down. When this call returns, the /// `JoinSet` will be empty. /// + /// Note that tasks can only abort once they reach an `await` point, this + /// might take a while for tasks containing blocking code even if they are + /// spawned with [`spawn_blocking`](Self::spawn_blocking). + /// /// [`abort_all`]: fn@Self::abort_all /// [`join_next`]: fn@Self::join_next pub async fn shutdown(&mut self) { @@ -451,6 +462,10 @@ impl JoinSet { /// /// This does not remove the tasks from the `JoinSet`. To wait for the tasks to complete /// cancellation, you should call `join_next` in a loop until the `JoinSet` is empty. + /// + /// Note that tasks can only abort once they reach an `await` point, this + /// might take a while for tasks containing blocking code even if they are + /// spawned with [`spawn_blocking`](Self::spawn_blocking). pub fn abort_all(&mut self) { self.inner.for_each(|jh| jh.abort()); } From 8291c1541e20ceb65e100de537414f22ef9410cf Mon Sep 17 00:00:00 2001 From: dvdsk Date: Tue, 27 Aug 2024 23:08:38 +0200 Subject: [PATCH 2/5] JoinSet docs removes erroneous mentions of .await --- tokio/src/task/join_set.rs | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/tokio/src/task/join_set.rs b/tokio/src/task/join_set.rs index 5e66875cc8e..cfa0abd9584 100644 --- a/tokio/src/task/join_set.rs +++ b/tokio/src/task/join_set.rs @@ -23,10 +23,10 @@ use crate::util::IdleNotifiedSet; /// /// All of the tasks must have the same return type `T`. /// -/// When the `JoinSet` is dropped, all non-blocking tasks in the `JoinSet` are -/// immediately aborted. Tasks spawned with -/// [`spawn_blocking`](Self::spawn_blocking) will abort when they reach an -/// `await` point +/// When the `JoinSet` is dropped, all *async* tasks in the `JoinSet` are +/// immediately aborted. Tasks spawned with [`spawn_blocking`] or +/// [`spawn_blocking_on`] can not be aborted as they are not *async*, see [task +/// cancellation]. /// /// # Examples /// @@ -54,6 +54,9 @@ use crate::util::IdleNotifiedSet; /// } /// } /// ``` +/// [`spawn_blocking`]: fn@Self::spawn_blocking +/// [`spawn_blocking_on`]: fn@Self::spawn_blocking_on +/// [task cancellation]: crate::task#cancellation #[cfg_attr(docsrs, doc(cfg(feature = "rt")))] pub struct JoinSet { inner: IdleNotifiedSet>, @@ -208,7 +211,8 @@ impl JoinSet { /// it in this `JoinSet`, returning an [`AbortHandle`] that can be /// used to remotely cancel the task. /// - /// Note that the task can only abort once it reaches an `await` point. + /// Note that this task can not be aborted as its not *async*, + /// see [task cancellation]. /// /// # Examples /// @@ -242,6 +246,7 @@ impl JoinSet { /// This method panics if called outside of a Tokio runtime. /// /// [`AbortHandle`]: crate::task::AbortHandle + /// [task cancellation]: crate::task#cancellation #[track_caller] pub fn spawn_blocking(&mut self, f: F) -> AbortHandle where @@ -256,7 +261,8 @@ impl JoinSet { /// provided runtime and store it in this `JoinSet`, returning an /// [`AbortHandle`] that can be used to remotely cancel the task. /// - /// Note that the task can only abort once it reaches an `await` point. + /// Note that this task can not be aborted as its not *async*, + /// see [task cancellation]. /// /// [`AbortHandle`]: crate::task::AbortHandle #[track_caller] @@ -374,12 +380,15 @@ impl JoinSet { /// This method ignores any panics in the tasks shutting down. When this call returns, the /// `JoinSet` will be empty. /// - /// Note that tasks can only abort once they reach an `await` point, this - /// might take a while for tasks containing blocking code even if they are - /// spawned with [`spawn_blocking`](Self::spawn_blocking). + /// Note that tasks spawned with [`spawn_blocking`] or [`spawn_blocking_on`] + /// can not be aborted as they are not *async*, see [task cancellation]. + /// They may cause the call to shutdown to block. /// /// [`abort_all`]: fn@Self::abort_all /// [`join_next`]: fn@Self::join_next + /// [`spawn_blocking`]: fn@Self::spawn_blocking + /// [`spawn_blocking_on`]: fn@Self::spawn_blocking_on + /// [task cancellation]: crate::task#cancellation pub async fn shutdown(&mut self) { self.abort_all(); while self.join_next().await.is_some() {} @@ -463,9 +472,13 @@ impl JoinSet { /// This does not remove the tasks from the `JoinSet`. To wait for the tasks to complete /// cancellation, you should call `join_next` in a loop until the `JoinSet` is empty. /// - /// Note that tasks can only abort once they reach an `await` point, this - /// might take a while for tasks containing blocking code even if they are - /// spawned with [`spawn_blocking`](Self::spawn_blocking). + /// Note that tasks spawned with [`spawn_blocking`] or [`spawn_blocking_on`] + /// can not be aborted as they are not *async*, see [task cancellation]. + /// They may cause the call to shutdown to block. + /// + /// [`spawn_blocking`]: fn@Self::spawn_blocking + /// [`spawn_blocking_on`]: fn@Self::spawn_blocking_on + /// [task cancellation]: crate::task#cancellation pub fn abort_all(&mut self) { self.inner.for_each(|jh| jh.abort()); } From 27c889c74a54ed8e7ea2c1967ab87986d0585b7b Mon Sep 17 00:00:00 2001 From: dvdsk Date: Fri, 30 Aug 2024 01:49:28 +0200 Subject: [PATCH 3/5] Joinset, note that abort is possible before tasks start running --- tokio/src/task/join_set.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tokio/src/task/join_set.rs b/tokio/src/task/join_set.rs index cfa0abd9584..bbcc0d16aa4 100644 --- a/tokio/src/task/join_set.rs +++ b/tokio/src/task/join_set.rs @@ -25,8 +25,8 @@ use crate::util::IdleNotifiedSet; /// /// When the `JoinSet` is dropped, all *async* tasks in the `JoinSet` are /// immediately aborted. Tasks spawned with [`spawn_blocking`] or -/// [`spawn_blocking_on`] can not be aborted as they are not *async*, see [task -/// cancellation]. +/// [`spawn_blocking_on`] can only be aborted before they start running. +/// This is because they are not *async*, see [task cancellation]. /// /// # Examples /// @@ -211,8 +211,8 @@ impl JoinSet { /// it in this `JoinSet`, returning an [`AbortHandle`] that can be /// used to remotely cancel the task. /// - /// Note that this task can not be aborted as its not *async*, - /// see [task cancellation]. + /// Note that this task can only be aborted before it starts running. + /// This is because they are not *async*, see [task cancellation]. /// /// # Examples /// @@ -261,8 +261,8 @@ impl JoinSet { /// provided runtime and store it in this `JoinSet`, returning an /// [`AbortHandle`] that can be used to remotely cancel the task. /// - /// Note that this task can not be aborted as its not *async*, - /// see [task cancellation]. + /// Note that this task can only be aborted before it starts running. + /// This is because they are not *async*, see [task cancellation]. /// /// [`AbortHandle`]: crate::task::AbortHandle #[track_caller] @@ -381,8 +381,9 @@ impl JoinSet { /// `JoinSet` will be empty. /// /// Note that tasks spawned with [`spawn_blocking`] or [`spawn_blocking_on`] - /// can not be aborted as they are not *async*, see [task cancellation]. - /// They may cause the call to shutdown to block. + /// can not be aborted after they start running. This is because they are not + /// *async*, see [task cancellation]. These tasks may cause the call to shutdown to + /// block. /// /// [`abort_all`]: fn@Self::abort_all /// [`join_next`]: fn@Self::join_next @@ -473,8 +474,9 @@ impl JoinSet { /// cancellation, you should call `join_next` in a loop until the `JoinSet` is empty. /// /// Note that tasks spawned with [`spawn_blocking`] or [`spawn_blocking_on`] - /// can not be aborted as they are not *async*, see [task cancellation]. - /// They may cause the call to shutdown to block. + /// can not be aborted after they start running. This is because they are not + /// *async*, see [task cancellation]. These tasks may cause the call to shutdown to + /// block. /// /// [`spawn_blocking`]: fn@Self::spawn_blocking /// [`spawn_blocking_on`]: fn@Self::spawn_blocking_on From 6cca2eb783d8219d8b6e19f478809eb384a0c1fa Mon Sep 17 00:00:00 2001 From: dvdsk Date: Fri, 30 Aug 2024 02:07:35 +0200 Subject: [PATCH 4/5] fmt --- tokio/src/task/join_set.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tokio/src/task/join_set.rs b/tokio/src/task/join_set.rs index bbcc0d16aa4..299e112336e 100644 --- a/tokio/src/task/join_set.rs +++ b/tokio/src/task/join_set.rs @@ -25,7 +25,7 @@ use crate::util::IdleNotifiedSet; /// /// When the `JoinSet` is dropped, all *async* tasks in the `JoinSet` are /// immediately aborted. Tasks spawned with [`spawn_blocking`] or -/// [`spawn_blocking_on`] can only be aborted before they start running. +/// [`spawn_blocking_on`] can only be aborted before they start running. /// This is because they are not *async*, see [task cancellation]. /// /// # Examples @@ -381,8 +381,8 @@ impl JoinSet { /// `JoinSet` will be empty. /// /// Note that tasks spawned with [`spawn_blocking`] or [`spawn_blocking_on`] - /// can not be aborted after they start running. This is because they are not - /// *async*, see [task cancellation]. These tasks may cause the call to shutdown to + /// can not be aborted after they start running. This is because they are not + /// *async*, see [task cancellation]. These tasks may cause the call to shutdown to /// block. /// /// [`abort_all`]: fn@Self::abort_all @@ -474,8 +474,8 @@ impl JoinSet { /// cancellation, you should call `join_next` in a loop until the `JoinSet` is empty. /// /// Note that tasks spawned with [`spawn_blocking`] or [`spawn_blocking_on`] - /// can not be aborted after they start running. This is because they are not - /// *async*, see [task cancellation]. These tasks may cause the call to shutdown to + /// can not be aborted after they start running. This is because they are not + /// *async*, see [task cancellation]. These tasks may cause the call to shutdown to /// block. /// /// [`spawn_blocking`]: fn@Self::spawn_blocking From 6f839e32c362e7c9c73746f501b10d249b614c36 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Tue, 3 Sep 2024 14:07:52 +0200 Subject: [PATCH 5/5] JoinSet: clearify docs fix docs link Co-authored-by: Motoyuki Kimura --- tokio/src/task/join_set.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tokio/src/task/join_set.rs b/tokio/src/task/join_set.rs index 299e112336e..e95b3ad7379 100644 --- a/tokio/src/task/join_set.rs +++ b/tokio/src/task/join_set.rs @@ -265,6 +265,7 @@ impl JoinSet { /// This is because they are not *async*, see [task cancellation]. /// /// [`AbortHandle`]: crate::task::AbortHandle + /// [task cancellation]: crate::task#cancellation #[track_caller] pub fn spawn_blocking_on(&mut self, f: F, handle: &Handle) -> AbortHandle where