1use std::{future::IntoFuture, num::NonZeroU64};
4
5use futures_channel::oneshot;
6use futures_core::Future;
7use glib::{prelude::*, translate::*};
8
9use crate::{Cancellable, ffi};
10
11#[derive(Debug, Eq, PartialEq)]
15#[repr(transparent)]
16pub struct CancelledHandlerId(NonZeroU64);
17
18impl CancelledHandlerId {
19 #[allow(clippy::missing_safety_doc)]
22 pub unsafe fn as_raw(&self) -> libc::c_ulong {
23 self.0.get() as libc::c_ulong
24 }
25}
26
27impl TryFromGlib<libc::c_ulong> for CancelledHandlerId {
28 type Error = GlibNoneError;
29 #[inline]
30 unsafe fn try_from_glib(val: libc::c_ulong) -> Result<Self, GlibNoneError> {
31 NonZeroU64::new(val as _).map(Self).ok_or(GlibNoneError)
32 }
33}
34
35pub trait CancellableExtManual: IsA<Cancellable> {
36 #[doc(alias = "g_cancellable_connect")]
55 fn connect_cancelled<F: FnOnce(&Self) + Send + 'static>(
56 &self,
57 callback: F,
58 ) -> Option<CancelledHandlerId> {
59 unsafe extern "C" fn connect_trampoline<P: IsA<Cancellable>, F: FnOnce(&P)>(
60 this: *mut ffi::GCancellable,
61 callback: glib::ffi::gpointer,
62 ) {
63 unsafe {
64 let callback: &mut Option<F> = &mut *(callback as *mut Option<F>);
65 let callback = callback
66 .take()
67 .expect("Cancellable::cancel() closure called multiple times");
68 callback(Cancellable::from_glib_borrow(this).unsafe_cast_ref())
69 }
70 }
71
72 unsafe extern "C" fn destroy_closure<F>(ptr: glib::ffi::gpointer) {
73 unsafe {
74 let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
75 }
76 }
77
78 let callback: Box<Option<F>> = Box::new(Some(callback));
79 unsafe {
80 from_glib(ffi::g_cancellable_connect(
81 self.as_ptr() as *mut _,
82 Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
83 connect_trampoline::<Self, F> as *const (),
84 )),
85 Box::into_raw(callback) as *mut _,
86 Some(destroy_closure::<F>),
87 ))
88 }
89 }
90 #[doc(alias = "g_cancellable_connect")]
93 fn connect_cancelled_local<F: FnOnce(&Self) + 'static>(
94 &self,
95 callback: F,
96 ) -> Option<CancelledHandlerId> {
97 let callback = glib::thread_guard::ThreadGuard::new(callback);
98
99 self.connect_cancelled(move |obj| (callback.into_inner())(obj))
100 }
101 #[doc(alias = "g_cancellable_disconnect")]
110 fn disconnect_cancelled(&self, id: CancelledHandlerId) {
111 unsafe { ffi::g_cancellable_disconnect(self.as_ptr() as *mut _, id.as_raw()) };
112 }
113 fn future(&self) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>> {
117 struct Guard(Cancellable, Option<CancelledHandlerId>);
118
119 impl Drop for Guard {
120 fn drop(&mut self) {
121 if let Some(id) = self.1.take() {
122 self.0.disconnect_cancelled(id);
123 }
124 }
125 }
126
127 let cancellable = self.as_ref().clone();
128 let (tx, rx) = oneshot::channel();
129 let id = cancellable.connect_cancelled(move |_| {
130 let _ = tx.send(());
131 });
132 let guard = Guard(cancellable, id);
133
134 Box::pin(async move {
135 rx.await.unwrap();
136 drop(guard);
137 })
138 }
139 #[doc(alias = "g_cancellable_set_error_if_cancelled")]
149 fn set_error_if_cancelled(&self) -> Result<(), glib::Error> {
150 unsafe {
151 let mut error = std::ptr::null_mut();
152 let is_ok = ffi::g_cancellable_set_error_if_cancelled(
153 self.as_ref().to_glib_none().0,
154 &mut error,
155 );
156 debug_assert_eq!(is_ok == glib::ffi::GFALSE, error.is_null());
159 if error.is_null() {
160 Ok(())
161 } else {
162 Err(from_glib_full(error))
163 }
164 }
165 }
166}
167
168impl<O: IsA<Cancellable>> CancellableExtManual for O {}
169
170impl IntoFuture for Cancellable {
171 type Output = ();
172
173 type IntoFuture = std::pin::Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;
174
175 fn into_future(self) -> Self::IntoFuture {
176 self.future()
177 }
178}
179
180impl IntoFuture for &Cancellable {
181 type Output = ();
182
183 type IntoFuture = std::pin::Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;
184
185 fn into_future(self) -> Self::IntoFuture {
186 self.future()
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 use crate::prelude::*;
195
196 #[test]
197 fn cancellable_callback() {
198 let c = Cancellable::new();
199 let id = c.connect_cancelled(|_| {});
200 c.cancel(); c.disconnect_cancelled(id.unwrap());
202 }
203
204 #[test]
205 fn cancellable_callback_local() {
206 let c = Cancellable::new();
207 let id = c.connect_cancelled_local(|_| {});
208 c.cancel(); c.disconnect_cancelled(id.unwrap());
210 }
211
212 #[test]
213 fn cancellable_error_if_cancelled() {
214 let c = Cancellable::new();
215 c.cancel();
216 assert!(c.set_error_if_cancelled().is_err());
217 }
218
219 #[test]
220 fn cancellable_future() {
221 let c = Cancellable::new();
222 c.cancel();
223 glib::MainContext::new().block_on(c.future());
224 }
225
226 #[test]
227 fn cancellable_future_thread() {
228 let cancellable = Cancellable::new();
229 let c = cancellable.clone();
230 std::thread::spawn(move || c.cancel()).join().unwrap();
231 glib::MainContext::new().block_on(cancellable.future());
232 }
233
234 #[test]
235 fn cancellable_future_delayed() {
236 let ctx = glib::MainContext::new();
237 let c = Cancellable::new();
238 let (tx, rx) = oneshot::channel();
239 {
240 let c = c.clone();
241 ctx.spawn_local(async move {
242 c.future().await;
243 tx.send(()).unwrap();
244 });
245 }
246 std::thread::spawn(move || c.cancel()).join().unwrap();
247 ctx.block_on(rx).unwrap();
248 }
249}