-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Motivation Before we stabilize the `JoinSet` API, we intend to add a method for individual tasks in the `JoinSet` to be aborted. Because the `JoinHandle`s for the tasks spawned on a `JoinSet` are owned by the `JoinSet`, the user can no longer use them to abort tasks on the `JoinSet`. Therefore, we need another way to cause a remote abort of a task on a `JoinSet` without holding its `JoinHandle`. ## Solution This branch adds a new `AbortHandle` type in `tokio::task`, which represents the owned permission to remotely cancel a task, but _not_ to await its output. The `AbortHandle` type holds an additional reference to the task cell. A crate-private method is added to `JoinHandle` that returns an `AbortHandle` for the same task, incrementing its ref count. `AbortHandle` provides a single method, `AbortHandle::abort(self)`, that remotely cancels the task. Dropping an `AbortHandle` decrements the task's ref count but does not cancel it. The `AbortHandle` type is currently marked as unstable. The spawning methods on `JoinSet` are modified to return an `AbortHandle` that can be used to cancel the spawned task. ## Future Work - Currently, the `AbortHandle` type is _only_ available in the public API through a `JoinSet`. We could also make the `JoinHandle::abort_handle` method public, to allow users to use the `AbortHandle` type in other contexts. I didn't do that in this PR, because I wanted to make the API addition as minimal as possible, but we could make this method public later. - Currently, `AbortHandle` is not `Clone`. We could easily make it `Clone` by incrementing the task's ref count. Since this adds more trait impls to the API, we may want to be cautious about this, but I see no obvious reason we would need to remove a `Clone` implementation if one was added... - There's been some discussion of adding a `JoinMap` type that allows aborting tasks by key, and manages a hash map of keys to `AbortHandle`s, and removes the tasks from the map when they complete. This would make aborting by key much easier, since the user wouldn't have to worry about keeping the state of the map of abort handles and the tasks actually active on the `JoinSet` in sync. After thinking about it a bit, I thought this is probably best as a `tokio-util` API --- it can currently be implemented in `tokio-util` with the APIs added in `tokio` in this PR. - I noticed while working on this that `JoinSet::join_one` and `JoinSet::poll_join_one` return a cancelled `JoinError` when a task is cancelled. I'm not sure if I love this behavior --- it seems like it would be nicer to just skip cancelled tasks and continue polling. But, there are currently tests that expect a cancelled `JoinError` to be returned for each cancelled task, so I didn't want to change it in _this_ PR. I think this is worth revisiting before stabilizing the API, though? Signed-off-by: Eliza Weisman <[email protected]>
- Loading branch information
Showing
9 changed files
with
328 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use crate::runtime::task::RawTask; | ||
use std::fmt; | ||
use std::panic::{RefUnwindSafe, UnwindSafe}; | ||
|
||
/// An owned permission to abort a spawned task, without awaiting its completion. | ||
/// | ||
/// Unlike a [`JoinHandle`], an `AbortHandle` does *not* represent the | ||
/// permission to await the task's completion, only to terminate it. | ||
/// | ||
/// The task may be aborted by calling the [`AbortHandle::abort`] method. | ||
/// Dropping an `AbortHandle` releases the permission to terminate the task | ||
/// --- it does *not* abort the task. | ||
/// | ||
/// **Note**: This is an [unstable API][unstable]. The public API of this type | ||
/// may break in 1.x releases. See [the documentation on unstable | ||
/// features][unstable] for details. | ||
/// | ||
/// [unstable]: crate#unstable-features | ||
/// [`JoinHandle`]: crate::task::JoinHandle | ||
#[cfg_attr(docsrs, doc(cfg(all(feature = "rt", tokio_unstable))))] | ||
#[cfg_attr(not(tokio_unstable), allow(unreachable_pub))] | ||
pub struct AbortHandle { | ||
raw: Option<RawTask>, | ||
} | ||
|
||
impl AbortHandle { | ||
pub(super) fn new(raw: Option<RawTask>) -> Self { | ||
Self { raw } | ||
} | ||
|
||
/// Abort the task associated with the handle. | ||
/// | ||
/// Awaiting a cancelled task might complete as usual if the task was | ||
/// already completed at the time it was cancelled, but most likely it | ||
/// will fail with a [cancelled] `JoinError`. | ||
/// | ||
/// If the task was already cancelled, such as by [`JoinHandle::abort`], | ||
/// this method will do nothing. | ||
/// | ||
/// [cancelled]: method@super::error::JoinError::is_cancelled | ||
// the `AbortHandle` type is only publicly exposed when `tokio_unstable` is | ||
// enabled, but it is still defined for testing purposes. | ||
#[cfg_attr(not(tokio_unstable), allow(unreachable_pub))] | ||
pub fn abort(self) { | ||
if let Some(raw) = self.raw { | ||
raw.remote_abort(); | ||
} | ||
} | ||
} | ||
|
||
unsafe impl Send for AbortHandle {} | ||
unsafe impl Sync for AbortHandle {} | ||
|
||
impl UnwindSafe for AbortHandle {} | ||
impl RefUnwindSafe for AbortHandle {} | ||
|
||
impl fmt::Debug for AbortHandle { | ||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
fmt.debug_struct("AbortHandle").finish() | ||
} | ||
} | ||
|
||
impl Drop for AbortHandle { | ||
fn drop(&mut self) { | ||
if let Some(raw) = self.raw.take() { | ||
raw.drop_abort_handle(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.