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