Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JoinSet, clarify behaviour of blocking code under abort/drop #6802

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion tokio/src/task/join_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 *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].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, they are aborted. It just has not effect unless it happens before the spawn_blocking task even starts running.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the window between the task being spawned and it starting execution not extremely small? I fail to see the use case for abort. If I am to document it I would like to mention a case/example which helps the reader understand that window.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nevermind just thought of it, the executor might have been set up to only allow a few simultaneous blocking tasks or rather threads. Ill work that into the docs

///
/// # Examples
///
Expand Down Expand Up @@ -51,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<T> {
inner: IdleNotifiedSet<JoinHandle<T>>,
Expand Down Expand Up @@ -205,6 +211,9 @@ impl<T: 'static> JoinSet<T> {
/// 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].
///
/// # Examples
///
/// Spawn multiple blocking tasks and wait for them.
Expand Down Expand Up @@ -237,6 +246,7 @@ impl<T: 'static> JoinSet<T> {
/// 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<F>(&mut self, f: F) -> AbortHandle
where
Expand All @@ -251,6 +261,9 @@ impl<T: 'static> JoinSet<T> {
/// 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].
///
/// [`AbortHandle`]: crate::task::AbortHandle
#[track_caller]
pub fn spawn_blocking_on<F>(&mut self, f: F, handle: &Handle) -> AbortHandle
Expand Down Expand Up @@ -367,8 +380,15 @@ impl<T: 'static> JoinSet<T> {
/// This method ignores any panics in the tasks shutting down. When this call returns, the
/// `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.
///
/// [`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() {}
Expand Down Expand Up @@ -451,6 +471,14 @@ impl<T: 'static> JoinSet<T> {
///
/// 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 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());
}
Expand Down
Loading