gio/
cancellable_future.rs1use std::{
4 fmt::{Debug, Display},
5 future::Future,
6 pin::Pin,
7 sync::Arc,
8 task::{Context, Poll},
9};
10
11use futures_util::task::AtomicWaker;
12use pin_project_lite::pin_project;
13
14use crate::{Cancellable, CancelledHandlerId, IOErrorEnum, prelude::*};
15
16pub struct Cancelled;
19
20pin_project! {
21 pub struct CancellableFuture<F> {
38 #[pin]
39 future: F,
40
41 waker: Arc<AtomicWaker>,
42
43 waker_handler_cb: Option<CancelledHandlerId>,
44
45 cancellable: Cancellable,
46 }
47
48 impl<F> PinnedDrop for CancellableFuture<F> {
49 fn drop(this: Pin<&mut Self>) {
50 let this = this.project();
51
52 if let Some(handler) = this.waker_handler_cb.take() {
53 this.cancellable.disconnect_cancelled(handler);
54 }
55 }
56 }
57}
58
59impl<F> CancellableFuture<F> {
60 pub fn new(future: F, cancellable: Cancellable) -> Self {
67 Self {
68 future,
69 waker: Arc::default(),
70 waker_handler_cb: None,
71 cancellable,
72 }
73 }
74
75 #[inline]
85 pub fn is_cancelled(&self) -> bool {
86 self.cancellable.is_cancelled()
87 }
88
89 #[inline]
92 pub fn cancellable(&self) -> &Cancellable {
93 &self.cancellable
94 }
95}
96
97impl<F> Future for CancellableFuture<F>
98where
99 F: Future,
100{
101 type Output = Result<<F as Future>::Output, Cancelled>;
102
103 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
104 if self.is_cancelled() {
105 return Poll::Ready(Err(Cancelled));
106 }
107
108 let this = self.project();
109
110 this.waker.register(cx.waker());
111
112 match this.future.poll(cx) {
113 Poll::Ready(out) => {
114 if let Some(handler) = this.waker_handler_cb.take() {
115 this.cancellable.disconnect_cancelled(handler);
116 }
117
118 this.waker.take();
119
120 Poll::Ready(Ok(out))
121 }
122
123 Poll::Pending => {
124 if this.waker_handler_cb.is_none() {
125 let waker = Arc::clone(this.waker);
126
127 match this.cancellable.connect_cancelled(move |_| waker.wake()) {
128 Some(handler) => *this.waker_handler_cb = Some(handler),
129
130 None => return Poll::Ready(Err(Cancelled)),
131 }
132 }
133
134 Poll::Pending
135 }
136 }
137 }
138}
139
140impl From<Cancelled> for glib::Error {
141 fn from(_: Cancelled) -> Self {
142 glib::Error::new(IOErrorEnum::Cancelled, "Task cancelled")
143 }
144}
145
146impl std::error::Error for Cancelled {}
147
148impl Debug for Cancelled {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 write!(f, "Task cancelled")
151 }
152}
153
154impl Display for Cancelled {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 Debug::fmt(self, f)
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use std::{
163 sync::Arc,
164 task::{Wake, Waker},
165 };
166
167 use futures_channel::oneshot;
168
169 use super::{Cancellable, CancellableFuture, Cancelled, Context, Future, Poll};
170 use crate::prelude::*;
171
172 #[test]
173 fn cancellable_future_ok() {
174 let ctx = glib::MainContext::new();
175 let c = Cancellable::new();
176 let (tx, rx) = oneshot::channel();
177
178 {
179 ctx.spawn_local(async {
180 let cancellable_future = CancellableFuture::new(async { 42 }, c);
181 assert!(!cancellable_future.is_cancelled());
182
183 let result = cancellable_future.await;
184 assert!(matches!(result, Ok(42)));
185
186 tx.send(()).unwrap();
187 });
188 }
189
190 ctx.block_on(rx).unwrap()
191 }
192
193 #[test]
194 fn cancellable_future_cancel() {
195 let ctx = glib::MainContext::new();
196 let c = Cancellable::new();
197 let (tx, rx) = oneshot::channel();
198
199 {
200 let c = c.clone();
201 ctx.spawn_local(async move {
202 let cancellable_future = CancellableFuture::new(std::future::pending::<()>(), c);
203
204 let result = cancellable_future.await;
205 assert!(matches!(result, Err(Cancelled)));
206
207 tx.send(()).unwrap();
208 });
209 }
210
211 std::thread::spawn(move || c.cancel()).join().unwrap();
212
213 ctx.block_on(rx).unwrap();
214 }
215
216 #[test]
217 fn cancellable_future_releases_waker_on_drop() {
218 let noop_wake = Arc::new(utils::NoopWake);
219 let waker = Waker::from(Arc::clone(&noop_wake));
220 let mut cx = Context::from_waker(&waker);
221
222 let cancellable = Cancellable::new();
223
224 let mut cancellable_future = Box::pin(CancellableFuture::new(
225 std::future::pending::<()>(),
226 cancellable.clone(),
227 ));
228
229 assert_eq!(Arc::strong_count(&noop_wake), 2);
230 let _ = Future::poll(cancellable_future.as_mut(), &mut cx);
231 assert_eq!(Arc::strong_count(&noop_wake), 3);
232
233 drop(cancellable_future);
234 assert_eq!(Arc::strong_count(&noop_wake), 2);
235 }
236
237 #[test]
238 fn cancellable_future_releases_waker_on_ready() {
239 let noop_wake = Arc::new(utils::NoopWake);
240 let waker = Waker::from(Arc::clone(&noop_wake));
241 let mut cx = Context::from_waker(&waker);
242
243 let cancellable = Cancellable::new();
244
245 let mut cancellable_future = Box::pin(CancellableFuture::new(
246 std::future::poll_fn({
247 let mut pending = true;
248
249 move |_| {
250 if std::mem::take(&mut pending) {
251 Poll::Pending
252 } else {
253 Poll::Ready(())
254 }
255 }
256 }),
257 cancellable.clone(),
258 ));
259
260 assert_eq!(Arc::strong_count(&noop_wake), 2);
261
262 assert!(Future::poll(cancellable_future.as_mut(), &mut cx).is_pending());
263
264 assert_eq!(Arc::strong_count(&noop_wake), 3);
265
266 assert!(Future::poll(cancellable_future.as_mut(), &mut cx).is_ready());
267
268 assert_eq!(Arc::strong_count(&noop_wake), 2);
269 }
270
271 mod utils {
272 use super::*;
273
274 pub struct NoopWake;
275
276 #[allow(clippy::manual_noop_waker)]
278 impl Wake for NoopWake {
279 fn wake(self: Arc<Self>) {}
280 }
281 }
282}