1use std::{future::IntoFuture, num::NonZeroU64};
4
5use futures_channel::oneshot;
6use futures_core::Future;
7use glib::{prelude::*, translate::*};
8
9use crate::{ffi, Cancellable};
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
35mod sealed {
36 pub trait Sealed {}
37 impl<T: super::IsA<super::Cancellable>> Sealed for T {}
38}
39
40pub trait CancellableExtManual: sealed::Sealed + IsA<Cancellable> {
41 #[doc(alias = "g_cancellable_connect")]
60 fn connect_cancelled<F: FnOnce(&Self) + Send + 'static>(
61 &self,
62 callback: F,
63 ) -> Option<CancelledHandlerId> {
64 unsafe extern "C" fn connect_trampoline<P: IsA<Cancellable>, F: FnOnce(&P)>(
65 this: *mut ffi::GCancellable,
66 callback: glib::ffi::gpointer,
67 ) {
68 let callback: &mut Option<F> = &mut *(callback as *mut Option<F>);
69 let callback = callback
70 .take()
71 .expect("Cancellable::cancel() closure called multiple times");
72 callback(Cancellable::from_glib_borrow(this).unsafe_cast_ref())
73 }
74
75 unsafe extern "C" fn destroy_closure<F>(ptr: glib::ffi::gpointer) {
76 let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
77 }
78
79 let callback: Box<Option<F>> = Box::new(Some(callback));
80 unsafe {
81 from_glib(ffi::g_cancellable_connect(
82 self.as_ptr() as *mut _,
83 Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>(
84 connect_trampoline::<Self, F> as *const (),
85 )),
86 Box::into_raw(callback) as *mut _,
87 Some(destroy_closure::<F>),
88 ))
89 }
90 }
91 #[doc(alias = "g_cancellable_connect")]
94 fn connect_cancelled_local<F: FnOnce(&Self) + 'static>(
95 &self,
96 callback: F,
97 ) -> Option<CancelledHandlerId> {
98 let callback = glib::thread_guard::ThreadGuard::new(callback);
99
100 self.connect_cancelled(move |obj| (callback.into_inner())(obj))
101 }
102 #[doc(alias = "g_cancellable_disconnect")]
111 fn disconnect_cancelled(&self, id: CancelledHandlerId) {
112 unsafe { ffi::g_cancellable_disconnect(self.as_ptr() as *mut _, id.as_raw()) };
113 }
114 fn future(&self) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>> {
118 let cancellable = self.as_ref().clone();
119 let (tx, rx) = oneshot::channel();
120 let id = cancellable.connect_cancelled(move |_| {
121 let _ = tx.send(());
122 });
123 Box::pin(async move {
124 rx.await.unwrap();
125 if let Some(id) = id {
126 cancellable.disconnect_cancelled(id);
127 }
128 })
129 }
130 #[doc(alias = "g_cancellable_set_error_if_cancelled")]
140 fn set_error_if_cancelled(&self) -> Result<(), glib::Error> {
141 unsafe {
142 let mut error = std::ptr::null_mut();
143 let is_ok = ffi::g_cancellable_set_error_if_cancelled(
144 self.as_ref().to_glib_none().0,
145 &mut error,
146 );
147 debug_assert_eq!(is_ok == glib::ffi::GFALSE, error.is_null());
150 if error.is_null() {
151 Ok(())
152 } else {
153 Err(from_glib_full(error))
154 }
155 }
156 }
157}
158
159impl<O: IsA<Cancellable>> CancellableExtManual for O {}
160
161impl IntoFuture for Cancellable {
162 type Output = ();
163
164 type IntoFuture = std::pin::Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;
165
166 fn into_future(self) -> Self::IntoFuture {
167 self.future()
168 }
169}
170
171impl IntoFuture for &Cancellable {
172 type Output = ();
173
174 type IntoFuture = std::pin::Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;
175
176 fn into_future(self) -> Self::IntoFuture {
177 self.future()
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 use crate::prelude::*;
186
187 #[test]
188 fn cancellable_callback() {
189 let c = Cancellable::new();
190 let id = c.connect_cancelled(|_| {});
191 c.cancel(); c.disconnect_cancelled(id.unwrap());
193 }
194
195 #[test]
196 fn cancellable_callback_local() {
197 let c = Cancellable::new();
198 let id = c.connect_cancelled_local(|_| {});
199 c.cancel(); c.disconnect_cancelled(id.unwrap());
201 }
202
203 #[test]
204 fn cancellable_error_if_cancelled() {
205 let c = Cancellable::new();
206 c.cancel();
207 assert!(c.set_error_if_cancelled().is_err());
208 }
209
210 #[test]
211 fn cancellable_future() {
212 let c = Cancellable::new();
213 c.cancel();
214 glib::MainContext::new().block_on(c.future());
215 }
216
217 #[test]
218 fn cancellable_future_thread() {
219 let cancellable = Cancellable::new();
220 let c = cancellable.clone();
221 std::thread::spawn(move || c.cancel()).join().unwrap();
222 glib::MainContext::new().block_on(cancellable.future());
223 }
224
225 #[test]
226 fn cancellable_future_delayed() {
227 let ctx = glib::MainContext::new();
228 let c = Cancellable::new();
229 let (tx, rx) = oneshot::channel();
230 {
231 let c = c.clone();
232 ctx.spawn_local(async move {
233 c.future().await;
234 tx.send(()).unwrap();
235 });
236 }
237 std::thread::spawn(move || c.cancel()).join().unwrap();
238 ctx.block_on(rx).unwrap();
239 }
240}