1use std::future::Future;
9use std::pin::Pin;
10use std::sync::atomic::{AtomicBool, Ordering};
11use std::sync::{Arc, Mutex};
12use std::task::{Context, Poll, Waker};
13
14#[derive(Clone, Debug, Default)]
19pub struct CancellationToken {
20 inner: Arc<Inner>,
21}
22
23#[derive(Debug, Default)]
24struct Inner {
25 cancelled: AtomicBool,
26 wakers: Mutex<Vec<Waker>>,
27}
28
29impl CancellationToken {
30 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn cancel(&self) {
39 if !self.inner.cancelled.swap(true, Ordering::SeqCst) {
40 let mut wakers = lock(&self.inner.wakers);
41 for waker in wakers.drain(..) {
42 waker.wake();
43 }
44 }
45 }
46
47 pub fn is_cancelled(&self) -> bool {
49 self.inner.cancelled.load(Ordering::SeqCst)
50 }
51
52 pub fn cancelled(&self) -> Cancelled {
66 Cancelled {
67 token: self.clone(),
68 }
69 }
70}
71
72#[derive(Clone, Debug)]
78#[must_use = "a future does nothing unless awaited"]
79pub struct Cancelled {
80 token: CancellationToken,
81}
82
83impl Future for Cancelled {
84 type Output = ();
85
86 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
87 if self.token.is_cancelled() {
88 return Poll::Ready(());
89 }
90 let mut wakers = lock(&self.token.inner.wakers);
91 if self.token.is_cancelled() {
94 return Poll::Ready(());
95 }
96 if !wakers.iter().any(|waker| waker.will_wake(cx.waker())) {
97 wakers.push(cx.waker().clone());
98 }
99 Poll::Pending
100 }
101}
102
103fn lock(mutex: &Mutex<Vec<Waker>>) -> std::sync::MutexGuard<'_, Vec<Waker>> {
106 mutex
107 .lock()
108 .unwrap_or_else(|poisoned| poisoned.into_inner())
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn cancel_is_visible_through_clones() {
117 let token = CancellationToken::new();
118 let clone = token.clone();
119 assert!(!clone.is_cancelled());
120 token.cancel();
121 assert!(clone.is_cancelled());
122 }
123
124 #[tokio::test]
125 async fn cancelled_future_resolves() {
126 let token = CancellationToken::new();
127 let waiter = token.clone();
128 let handle = tokio::spawn(async move { waiter.cancelled().await });
129 token.cancel();
130 handle.await.expect("waiter task panicked");
131 }
132
133 #[tokio::test]
134 async fn cancelled_future_is_ready_when_already_cancelled() {
135 let token = CancellationToken::new();
136 token.cancel();
137 token.cancelled().await;
138 }
139}