Skip to content

Commit 1ac8dff

Browse files
authored
task: add AbortOnDropHandle type (tokio-rs#6786)
1 parent ff3f2a8 commit 1ac8dff

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

tokio-util/src/task/abort_on_drop.rs

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//! An [`AbortOnDropHandle`] is like a [`JoinHandle`], except that it
2+
//! will abort the task as soon as it is dropped.
3+
4+
use tokio::task::{AbortHandle, JoinError, JoinHandle};
5+
6+
use std::{
7+
future::Future,
8+
pin::Pin,
9+
task::{Context, Poll},
10+
};
11+
12+
/// A wrapper around a [`tokio::task::JoinHandle`],
13+
/// which [aborts] the task when it is dropped.
14+
///
15+
/// [aborts]: tokio::task::JoinHandle::abort
16+
#[must_use = "Dropping the handle aborts the task immediately"]
17+
#[derive(Debug)]
18+
pub struct AbortOnDropHandle<T>(JoinHandle<T>);
19+
20+
impl<T> Drop for AbortOnDropHandle<T> {
21+
fn drop(&mut self) {
22+
self.0.abort()
23+
}
24+
}
25+
26+
impl<T> AbortOnDropHandle<T> {
27+
/// Create an [`AbortOnDropHandle`] from a [`JoinHandle`].
28+
pub fn new(handle: JoinHandle<T>) -> Self {
29+
Self(handle)
30+
}
31+
32+
/// Abort the task associated with this handle,
33+
/// equivalent to [`JoinHandle::abort`].
34+
pub fn abort(&self) {
35+
self.0.abort()
36+
}
37+
38+
/// Checks if the task associated with this handle is finished,
39+
/// equivalent to [`JoinHandle::is_finished`].
40+
pub fn is_finished(&self) -> bool {
41+
self.0.is_finished()
42+
}
43+
44+
/// Returns a new [`AbortHandle`] that can be used to remotely abort this task,
45+
/// equivalent to [`JoinHandle::abort_handle`].
46+
pub fn abort_handle(&self) -> AbortHandle {
47+
self.0.abort_handle()
48+
}
49+
}
50+
51+
impl<T> Future for AbortOnDropHandle<T> {
52+
type Output = Result<T, JoinError>;
53+
54+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
55+
Pin::new(&mut self.0).poll(cx)
56+
}
57+
}
58+
59+
impl<T> AsRef<JoinHandle<T>> for AbortOnDropHandle<T> {
60+
fn as_ref(&self) -> &JoinHandle<T> {
61+
&self.0
62+
}
63+
}

tokio-util/src/task/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ pub use join_map::{JoinMap, JoinMapKeys};
1111

1212
pub mod task_tracker;
1313
pub use task_tracker::TaskTracker;
14+
15+
mod abort_on_drop;
16+
pub use abort_on_drop::AbortOnDropHandle;

tokio-util/tests/abort_on_drop.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use tokio::sync::oneshot;
2+
use tokio_util::task::AbortOnDropHandle;
3+
4+
#[tokio::test]
5+
async fn aborts_task_on_drop() {
6+
let (mut tx, rx) = oneshot::channel::<bool>();
7+
let handle = tokio::spawn(async move {
8+
let _ = rx.await;
9+
});
10+
let handle = AbortOnDropHandle::new(handle);
11+
drop(handle);
12+
tx.closed().await;
13+
assert!(tx.is_closed());
14+
}
15+
16+
#[tokio::test]
17+
async fn aborts_task_directly() {
18+
let (mut tx, rx) = oneshot::channel::<bool>();
19+
let handle = tokio::spawn(async move {
20+
let _ = rx.await;
21+
});
22+
let handle = AbortOnDropHandle::new(handle);
23+
handle.abort();
24+
tx.closed().await;
25+
assert!(tx.is_closed());
26+
assert!(handle.is_finished());
27+
}

0 commit comments

Comments
 (0)