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