1use std::{future::Future, pin::Pin, ptr};
4
5use glib::{prelude::*, subclass::prelude::*, thread_guard::ThreadGuard, translate::*, Error};
6
7use crate::{
8 ffi, AsyncInitable, AsyncResult, Cancellable, CancellableFuture, GioFutureResult, LocalTask,
9};
10
11pub trait AsyncInitableImpl: ObjectImpl {
12 fn init_future(
13 &self,
14 io_priority: glib::Priority,
15 ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + 'static>> {
16 self.parent_init_future(io_priority)
17 }
18}
19
20mod sealed {
21 pub trait Sealed {}
22 impl<T: super::AsyncInitableImplExt> Sealed for T {}
23}
24
25pub trait AsyncInitableImplExt: sealed::Sealed + AsyncInitableImpl + ObjectSubclass {
26 fn parent_init_future(
27 &self,
28 io_priority: glib::Priority,
29 ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + 'static>> {
30 unsafe {
31 let type_data = Self::type_data();
32 let parent_iface = type_data.as_ref().parent_interface::<AsyncInitable>()
33 as *const ffi::GAsyncInitableIface;
34
35 let init_async = (*parent_iface)
36 .init_async
37 .expect("no parent \"init_async\" implementation");
38
39 unsafe extern "C" fn parent_init_future_callback<T>(
40 source_object: *mut glib::gobject_ffi::GObject,
41 res: *mut crate::ffi::GAsyncResult,
42 user_data: glib::ffi::gpointer,
43 ) where
44 T: AsyncInitableImpl,
45 {
46 let type_data = T::type_data();
47 let parent_iface = type_data.as_ref().parent_interface::<AsyncInitable>()
48 as *const ffi::GAsyncInitableIface;
49 let init_finish = (*parent_iface)
50 .init_finish
51 .expect("no parent \"init_finish\" implementation");
52
53 let r: Box<ThreadGuard<GioFutureResult<Result<(), Error>>>> =
54 Box::from_raw(user_data as *mut _);
55 let r = r.into_inner();
56
57 let mut error = ptr::null_mut();
58 init_finish(source_object as *mut _, res, &mut error);
59 let result = if error.is_null() {
60 Ok(())
61 } else {
62 Err(from_glib_full(error))
63 };
64 r.resolve(result);
65 }
66
67 Box::pin(crate::GioFuture::new(
68 &*self.obj(),
69 move |obj, cancellable, res| {
70 let user_data: Box<ThreadGuard<GioFutureResult<Result<(), Error>>>> =
71 Box::new(ThreadGuard::new(res));
72 let user_data = Box::into_raw(user_data);
73 init_async(
74 obj.unsafe_cast_ref::<AsyncInitable>().to_glib_none().0,
75 io_priority.into_glib(),
76 cancellable.to_glib_none().0,
77 Some(parent_init_future_callback::<Self>),
78 user_data as *mut _,
79 );
80 },
81 ))
82 }
83 }
84}
85
86impl<T: AsyncInitableImpl> AsyncInitableImplExt for T {}
87
88unsafe impl<T: AsyncInitableImpl> IsImplementable<T> for AsyncInitable {
89 fn interface_init(iface: &mut glib::Interface<Self>) {
90 let iface = iface.as_mut();
91 iface.init_async = Some(async_initable_init_async::<T>);
92 iface.init_finish = Some(async_initable_init_finish);
93 }
94}
95
96unsafe extern "C" fn async_initable_init_async<T: AsyncInitableImpl>(
97 initable: *mut ffi::GAsyncInitable,
98 io_priority: std::os::raw::c_int,
99 cancellable: *mut ffi::GCancellable,
100 callback: ffi::GAsyncReadyCallback,
101 user_data: glib::ffi::gpointer,
102) {
103 let instance = &*(initable as *mut T::Instance);
104 let imp = instance.imp();
105 let cancellable = Option::<Cancellable>::from_glib_none(cancellable);
106
107 let task = callback.map(|callback| {
108 let task = LocalTask::new(
109 Some(imp.obj().unsafe_cast_ref::<glib::Object>()),
110 cancellable.as_ref(),
111 move |task, obj| {
112 let result: *mut crate::ffi::GAsyncResult =
113 task.upcast_ref::<AsyncResult>().to_glib_none().0;
114 let obj: *mut glib::gobject_ffi::GObject = obj.to_glib_none().0;
115 callback(obj, result, user_data);
116 },
117 );
118 task.set_check_cancellable(true);
119 task.set_return_on_cancel(true);
120 task
121 });
122
123 glib::MainContext::ref_thread_default().spawn_local(async move {
124 let io_priority = from_glib(io_priority);
125 let res = if let Some(cancellable) = cancellable {
126 CancellableFuture::new(imp.init_future(io_priority), cancellable)
127 .await
128 .map_err(|cancelled| cancelled.into())
129 .and_then(|res| res)
130 } else {
131 imp.init_future(io_priority).await
132 };
133 if let Some(task) = task {
134 task.return_result(res.map(|_t| true));
135 }
136 });
137}
138
139unsafe extern "C" fn async_initable_init_finish(
140 initable: *mut ffi::GAsyncInitable,
141 res: *mut ffi::GAsyncResult,
142 error: *mut *mut glib::ffi::GError,
143) -> glib::ffi::gboolean {
144 let res = from_glib_none::<_, AsyncResult>(res);
145
146 let task = res
147 .downcast::<LocalTask<bool>>()
148 .expect("GAsyncResult is not a GTask");
149 if !LocalTask::<bool>::is_valid(
150 &task,
151 Some(from_glib_borrow::<_, AsyncInitable>(initable).as_ref()),
152 ) {
153 panic!("Task is not valid for source object");
154 }
155
156 match task.propagate() {
157 Ok(v) => {
158 debug_assert!(v);
159 true.into_glib()
160 }
161 Err(e) => {
162 if !error.is_null() {
163 *error = e.into_glib_ptr();
164 }
165 false.into_glib()
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate::prelude::*;
174
175 pub mod imp {
176 use std::cell::Cell;
177
178 use super::*;
179
180 pub struct AsyncInitableTestType(pub Cell<u64>);
181
182 #[glib::object_subclass]
183 impl ObjectSubclass for AsyncInitableTestType {
184 const NAME: &'static str = "AsyncInitableTestType";
185 type Type = super::AsyncInitableTestType;
186 type Interfaces = (AsyncInitable,);
187
188 fn new() -> Self {
189 Self(Cell::new(0))
190 }
191 }
192
193 impl AsyncInitableImpl for AsyncInitableTestType {
194 fn init_future(
195 &self,
196 _io_priority: glib::Priority,
197 ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + 'static>> {
198 let imp = glib::subclass::ObjectImplRef::new(self);
199 Box::pin(async move {
200 glib::timeout_future_seconds(0).await;
201 imp.0.set(0x123456789abcdef);
202 Ok(())
203 })
204 }
205 }
206
207 impl ObjectImpl for AsyncInitableTestType {}
208 }
209
210 pub mod ffi {
211 use super::*;
212 pub type AsyncInitableTestType = <imp::AsyncInitableTestType as ObjectSubclass>::Instance;
213
214 pub unsafe extern "C" fn async_initable_test_type_get_value(
215 this: *mut AsyncInitableTestType,
216 ) -> u64 {
217 let this = super::AsyncInitableTestType::from_glib_borrow(this);
218 this.imp().0.get()
219 }
220 }
221
222 glib::wrapper! {
223 pub struct AsyncInitableTestType(ObjectSubclass<imp::AsyncInitableTestType>)
224 @implements AsyncInitable;
225 }
226
227 #[allow(clippy::new_without_default)]
228 impl AsyncInitableTestType {
229 pub async fn new() -> Self {
230 AsyncInitable::new_future(glib::Priority::default())
231 .await
232 .expect("Failed creation/initialization of AsyncInitableTestType object")
233 }
234
235 pub unsafe fn new_uninit() -> Self {
236 glib::Object::new_internal(Self::static_type(), &mut [])
240 .downcast()
241 .unwrap()
242 }
243
244 pub fn value(&self) -> u64 {
245 self.imp().0.get()
246 }
247 }
248
249 #[test]
250 fn test_async_initable_with_init() {
251 glib::MainContext::new().block_on(async {
252 let res = unsafe {
253 let test = AsyncInitableTestType::new_uninit();
254
255 assert_ne!(0x123456789abcdef, test.value());
256
257 test.init_future(glib::Priority::default())
258 .await
259 .map(|_| test)
260 };
261 assert!(res.is_ok());
262 let test = res.unwrap();
263
264 assert_eq!(0x123456789abcdef, test.value());
265 });
266 }
267
268 #[test]
269 fn test_async_initable_with_initable_new() {
270 glib::MainContext::new().block_on(async {
271 let test = AsyncInitableTestType::new().await;
272 assert_eq!(0x123456789abcdef, test.value());
273 });
274 }
275
276 #[test]
277 #[should_panic = ""]
278 fn test_async_initable_new_failure() {
279 glib::MainContext::new().block_on(async {
280 let value: u32 = 2;
281 let _ = AsyncInitable::builder::<AsyncInitableTestType>()
282 .property("invalid-property", value)
283 .build_future(glib::Priority::default())
284 .await;
285 unreachable!();
286 });
287 }
288
289 #[test]
290 fn test_async_initable_with_initable_with_type() {
291 glib::MainContext::new().block_on(async {
292 let test = AsyncInitable::with_type_future(
293 AsyncInitableTestType::static_type(),
294 glib::Priority::default(),
295 )
296 .await
297 .expect("Failed creation/initialization of AsyncInitableTestType object from type")
298 .downcast::<AsyncInitableTestType>()
299 .expect("Failed downcast of AsyncInitableTestType object");
300 assert_eq!(0x123456789abcdef, test.value());
301 });
302 }
303
304 #[test]
305 fn test_async_initable_with_async_initable_with_values() {
306 glib::MainContext::new().block_on(async {
307 let test = AsyncInitable::with_type_future(
308 AsyncInitableTestType::static_type(),
309 glib::Priority::default(),
310 )
311 .await
312 .expect("Failed creation/initialization of AsyncInitableTestType object from values")
313 .downcast::<AsyncInitableTestType>()
314 .expect("Failed downcast of AsyncInitableTestType object");
315 assert_eq!(0x123456789abcdef, test.value());
316 });
317 }
318
319 #[test]
320 fn test_async_initable_through_ffi() {
321 use futures_channel::oneshot;
322
323 glib::MainContext::new().block_on(async {
324 unsafe {
325 let test = AsyncInitableTestType::new_uninit();
326 let test: *mut ffi::AsyncInitableTestType = test.as_ptr();
327
328 assert_ne!(
329 0x123456789abcdef,
330 ffi::async_initable_test_type_get_value(test)
331 );
332
333 let (tx, rx) = oneshot::channel::<Result<(), glib::Error>>();
334 let user_data = Box::new(ThreadGuard::new(tx));
335 unsafe extern "C" fn init_async_callback(
336 source_object: *mut glib::gobject_ffi::GObject,
337 res: *mut crate::ffi::GAsyncResult,
338 user_data: glib::ffi::gpointer,
339 ) {
340 let tx: Box<ThreadGuard<oneshot::Sender<Result<(), glib::Error>>>> =
341 Box::from_raw(user_data as *mut _);
342 let tx = tx.into_inner();
343 let mut error = ptr::null_mut();
344 let ret = crate::ffi::g_async_initable_init_finish(
345 source_object as *mut _,
346 res,
347 &mut error,
348 );
349 assert_eq!(ret, glib::ffi::GTRUE);
350 let result = if error.is_null() {
351 Ok(())
352 } else {
353 Err(from_glib_full(error))
354 };
355 tx.send(result).unwrap();
356 }
357
358 crate::ffi::g_async_initable_init_async(
359 test as *mut crate::ffi::GAsyncInitable,
360 glib::ffi::G_PRIORITY_DEFAULT,
361 std::ptr::null_mut(),
362 Some(init_async_callback),
363 Box::into_raw(user_data) as *mut _,
364 );
365
366 let result = rx.await.unwrap();
367 assert!(result.is_ok());
368 assert_eq!(
369 0x123456789abcdef,
370 ffi::async_initable_test_type_get_value(test)
371 );
372 }
373 });
374 }
375}