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 all 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
33 changes: 32 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 only be aborted before they start running.
/// This is because 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.

Why is "async" italic every time? We don't usually do that.

Copy link
Contributor Author

@dvdsk dvdsk Sep 3, 2024

Choose a reason for hiding this comment

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

I wanted to highlight the word to stress that it is the difference between async and blocking tasks that leads to the different behaviour. Its just a formatting thing, if it conflicts with the usual docs style it can be removed.

///
/// # 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 only be aborted before it starts running.
/// This is because they are 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,7 +261,11 @@ 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 only be aborted before it starts running.
/// 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<F>(&mut self, f: F, handle: &Handle) -> AbortHandle
where
Expand Down Expand Up @@ -367,8 +381,16 @@ 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 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
/// [`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 +473,15 @@ 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 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
/// [task cancellation]: crate::task#cancellation
pub fn abort_all(&mut self) {
self.inner.for_each(|jh| jh.abort());
}
Expand Down
Loading