gio/
task.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{boxed::Box as Box_, future::Future, mem::transmute, panic, ptr};
4
5use glib::{
6    prelude::*,
7    signal::{connect_raw, SignalHandlerId},
8    translate::*,
9};
10
11use futures_channel::oneshot;
12
13use crate::{ffi, AsyncResult, Cancellable};
14
15glib::wrapper! {
16    // rustdoc-stripper-ignore-next
17    /// `LocalTask` provides idiomatic access to gio's `GTask` API, for
18    /// instance by being generic over their value type, while not completely departing
19    /// from the underlying C API. `LocalTask` does not require its value to be `Send`
20    /// and `Sync` and thus is useful to to implement gio style asynchronous
21    /// tasks that run in the glib main loop. If you need to run tasks in threads
22    /// see the `Task` type.
23    ///
24    /// The constructors of `LocalTask` and `Task` is marked as unsafe because this API does
25    /// not allow to automatically enforce all the invariants required to be a completely
26    /// safe abstraction. See the `Task` type for more details.
27    #[doc(alias = "GTask")]
28    pub struct LocalTask<V: ValueType>(Object<ffi::GTask, ffi::GTaskClass>) @implements AsyncResult;
29
30    match fn {
31        type_ => || ffi::g_task_get_type(),
32    }
33}
34
35glib::wrapper! {
36    // rustdoc-stripper-ignore-next
37    /// `Task` provides idiomatic access to gio's `GTask` API, for
38    /// instance by being generic over their value type, while not completely departing
39    /// from the underlying C API. `Task` is `Send` and `Sync` and requires its value to
40    /// also be `Send` and `Sync`, thus is useful to to implement gio style asynchronous
41    /// tasks that run in threads. If you need to only run tasks in glib main loop
42    /// see the `LocalTask` type.
43    ///
44    /// The constructors of `LocalTask` and `Task` is marked as unsafe because this API does
45    /// not allow to automatically enforce all the invariants required to be a completely
46    /// safe abstraction. The caller is responsible to ensure the following requirements
47    /// are satisfied
48    ///
49    /// * You should not create a `LocalTask`, upcast it to a `glib::Object` and then
50    ///   downcast it to a `Task`, as this will bypass the thread safety requirements
51    /// * You should ensure that the `return_result`, `return_error_if_cancelled` and
52    ///   `propagate()` methods are only called once.
53    #[doc(alias = "GTask")]
54    pub struct Task<V: ValueType + Send>(Object<ffi::GTask, ffi::GTaskClass>) @implements AsyncResult;
55
56    match fn {
57        type_ => || ffi::g_task_get_type(),
58    }
59}
60
61macro_rules! task_impl {
62    ($name:ident $(, @bound: $bound:tt)? $(, @safety: $safety:tt)?) => {
63        impl <V: Into<glib::Value> + ValueType $(+ $bound)?> $name<V> {
64            #[doc(alias = "g_task_new")]
65            #[allow(unused_unsafe)]
66            pub unsafe fn new<S, P, Q>(
67                source_object: Option<&S>,
68                cancellable: Option<&P>,
69                callback: Q,
70            ) -> Self
71            where
72                S: IsA<glib::Object> $(+ $bound)?,
73                P: IsA<Cancellable>,
74                Q: FnOnce($name<V>, Option<&S>) $(+ $bound)? + 'static,
75            {
76                let callback_data = Box_::new(callback);
77                unsafe extern "C" fn trampoline<
78                    S: IsA<glib::Object> $(+ $bound)?,
79                    V: ValueType $(+ $bound)?,
80                    Q: FnOnce($name<V>, Option<&S>) $(+ $bound)? + 'static,
81                >(
82                    source_object: *mut glib::gobject_ffi::GObject,
83                    res: *mut ffi::GAsyncResult,
84                    user_data: glib::ffi::gpointer,
85                ) {
86                    let callback: Box_<Q> = Box::from_raw(user_data as *mut _);
87                    let task = AsyncResult::from_glib_none(res)
88                        .downcast::<$name<V>>()
89                        .unwrap();
90                    let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
91                    callback(
92                        task,
93                        source_object.as_ref().as_ref().map(|s| s.unsafe_cast_ref()),
94                    );
95                }
96                let callback = trampoline::<S, V, Q>;
97                unsafe {
98                    from_glib_full(ffi::g_task_new(
99                        source_object.map(|p| p.as_ref()).to_glib_none().0,
100                        cancellable.map(|p| p.as_ref()).to_glib_none().0,
101                        Some(callback),
102                        Box_::into_raw(callback_data) as *mut _,
103                    ))
104                }
105            }
106
107            #[doc(alias = "g_task_get_cancellable")]
108            #[doc(alias = "get_cancellable")]
109            pub fn cancellable(&self) -> Option<Cancellable> {
110                unsafe { from_glib_none(ffi::g_task_get_cancellable(self.to_glib_none().0)) }
111            }
112
113            #[doc(alias = "g_task_get_check_cancellable")]
114            #[doc(alias = "get_check_cancellable")]
115            pub fn is_check_cancellable(&self) -> bool {
116                unsafe { from_glib(ffi::g_task_get_check_cancellable(self.to_glib_none().0)) }
117            }
118
119            #[doc(alias = "g_task_set_check_cancellable")]
120            pub fn set_check_cancellable(&self, check_cancellable: bool) {
121                unsafe {
122                    ffi::g_task_set_check_cancellable(self.to_glib_none().0, check_cancellable.into_glib());
123                }
124            }
125
126            #[cfg(feature = "v2_60")]
127            #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
128            #[doc(alias = "g_task_set_name")]
129            pub fn set_name(&self, name: Option<&str>) {
130                unsafe {
131                    ffi::g_task_set_name(self.to_glib_none().0, name.to_glib_none().0);
132                }
133            }
134
135            #[doc(alias = "g_task_set_return_on_cancel")]
136            pub fn set_return_on_cancel(&self, return_on_cancel: bool) -> bool {
137                unsafe {
138                    from_glib(ffi::g_task_set_return_on_cancel(
139                        self.to_glib_none().0,
140                        return_on_cancel.into_glib(),
141                    ))
142                }
143            }
144
145            #[doc(alias = "g_task_is_valid")]
146            pub fn is_valid(
147                result: &impl IsA<AsyncResult>,
148                source_object: Option<&impl IsA<glib::Object>>,
149            ) -> bool {
150                unsafe {
151                    from_glib(ffi::g_task_is_valid(
152                        result.as_ref().to_glib_none().0,
153                        source_object.map(|p| p.as_ref()).to_glib_none().0,
154                    ))
155                }
156            }
157
158            #[doc(alias = "get_priority")]
159            #[doc(alias = "g_task_get_priority")]
160            pub fn priority(&self) -> glib::source::Priority {
161                unsafe { FromGlib::from_glib(ffi::g_task_get_priority(self.to_glib_none().0)) }
162            }
163
164            #[doc(alias = "g_task_set_priority")]
165            pub fn set_priority(&self, priority: glib::source::Priority) {
166                unsafe {
167                    ffi::g_task_set_priority(self.to_glib_none().0, priority.into_glib());
168                }
169            }
170
171            #[doc(alias = "g_task_get_completed")]
172            #[doc(alias = "get_completed")]
173            pub fn is_completed(&self) -> bool {
174                unsafe { from_glib(ffi::g_task_get_completed(self.to_glib_none().0)) }
175            }
176
177            #[doc(alias = "g_task_get_context")]
178            #[doc(alias = "get_context")]
179            pub fn context(&self) -> glib::MainContext {
180                unsafe { from_glib_none(ffi::g_task_get_context(self.to_glib_none().0)) }
181            }
182
183            #[cfg(feature = "v2_60")]
184            #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
185            #[doc(alias = "g_task_get_name")]
186            #[doc(alias = "get_name")]
187            pub fn name(&self) -> Option<glib::GString> {
188                unsafe { from_glib_none(ffi::g_task_get_name(self.to_glib_none().0)) }
189            }
190
191            #[doc(alias = "g_task_get_return_on_cancel")]
192            #[doc(alias = "get_return_on_cancel")]
193            pub fn is_return_on_cancel(&self) -> bool {
194                unsafe { from_glib(ffi::g_task_get_return_on_cancel(self.to_glib_none().0)) }
195            }
196
197            #[doc(alias = "g_task_had_error")]
198            pub fn had_error(&self) -> bool {
199                unsafe { from_glib(ffi::g_task_had_error(self.to_glib_none().0)) }
200            }
201
202            #[doc(alias = "completed")]
203            pub fn connect_completed_notify<F>(&self, f: F) -> SignalHandlerId
204            where
205                F: Fn(&$name<V>) $(+ $bound)? + 'static,
206            {
207                unsafe extern "C" fn notify_completed_trampoline<V, F>(
208                    this: *mut ffi::GTask,
209                    _param_spec: glib::ffi::gpointer,
210                    f: glib::ffi::gpointer,
211                ) where
212                    V: ValueType $(+ $bound)?,
213                    F: Fn(&$name<V>) + 'static,
214                {
215                    let f: &F = &*(f as *const F);
216                    f(&from_glib_borrow(this))
217                }
218                unsafe {
219                    let f: Box_<F> = Box_::new(f);
220                    connect_raw(
221                        self.as_ptr() as *mut _,
222                        b"notify::completed\0".as_ptr() as *const _,
223                        Some(transmute::<*const (), unsafe extern "C" fn()>(
224                            notify_completed_trampoline::<V, F> as *const (),
225                        )),
226                        Box_::into_raw(f),
227                    )
228                }
229            }
230
231            // the following functions are marked unsafe since they cannot be called
232            // more than once, but we have no way to enforce that since the task can be cloned
233
234            #[doc(alias = "g_task_return_error_if_cancelled")]
235            #[allow(unused_unsafe)]
236            pub $($safety)? fn return_error_if_cancelled(&self) -> bool {
237                unsafe { from_glib(ffi::g_task_return_error_if_cancelled(self.to_glib_none().0)) }
238            }
239
240            // rustdoc-stripper-ignore-next
241            /// Set the result of the task
242            ///
243            /// # Safety
244            ///
245            /// The value must be read with [`Task::propagate`],
246            /// `g_task_propagate_value` or `g_task_propagate_pointer`.
247            #[doc(alias = "g_task_return_value")]
248            #[doc(alias = "g_task_return_pointer")]
249            #[doc(alias = "g_task_return_error")]
250            #[allow(unused_unsafe)]
251            pub $($safety)? fn return_result(self, result: Result<V, glib::Error>) {
252                #[cfg(not(feature = "v2_64"))]
253                unsafe extern "C" fn value_free(value: *mut libc::c_void) {
254                    let _: glib::Value = from_glib_full(value as *mut glib::gobject_ffi::GValue);
255                }
256
257                match result {
258                    #[cfg(feature = "v2_64")]
259                    Ok(v) => unsafe {
260                        ffi::g_task_return_value(
261                            self.to_glib_none().0,
262                            v.to_value().to_glib_none().0 as *mut _,
263                        )
264                    },
265                    #[cfg(not(feature = "v2_64"))]
266                    Ok(v) => unsafe {
267                        let v: glib::Value = v.into();
268                        ffi::g_task_return_pointer(
269                            self.to_glib_none().0,
270                            <glib::Value as glib::translate::IntoGlibPtr::<*mut glib::gobject_ffi::GValue>>::into_glib_ptr(v) as glib::ffi::gpointer,
271                            Some(value_free),
272                        )
273                    },
274                    Err(e) => unsafe {
275                        ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr());
276                    },
277                }
278            }
279
280            // rustdoc-stripper-ignore-next
281            /// Set the result of the task as a boolean
282            ///
283            /// # Safety
284            ///
285            /// The value must be read with [`Task::propagate_boolean`],
286            /// or `g_task_propagate_boolean`.
287            #[doc(alias = "g_task_return_boolean")]
288            #[allow(unused_unsafe)]
289            pub $($safety)? fn return_boolean_result(self, result: Result<bool, glib::Error>) {
290                match result {
291                    Ok(v) =>  unsafe { ffi::g_task_return_boolean(self.to_glib_none().0, v as i32) },
292                    Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) },
293                }
294            }
295
296            // rustdoc-stripper-ignore-next
297            /// Set the result of the task as an int
298            ///
299            /// # Safety
300            ///
301            /// The value must be read with [`Task::propagate_int`],
302            /// or `g_task_propagate_int`.
303            #[doc(alias = "g_task_return_int")]
304            #[allow(unused_unsafe)]
305            pub $($safety)? fn return_int_result(self, result: Result<isize, glib::Error>) {
306                match result {
307                    Ok(v) =>  unsafe { ffi::g_task_return_int(self.to_glib_none().0, v) },
308                    Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) },
309                }
310            }
311
312
313            // rustdoc-stripper-ignore-next
314            /// Gets the result of the task and transfers ownership of it
315            ///
316            /// # Safety
317            ///
318            /// This must only be called once, and only if the result was set
319            /// via [`Task::return_result`], `g_task_return_value` or
320            /// `g_task_return_pointer`.
321            #[doc(alias = "g_task_propagate_value")]
322            #[doc(alias = "g_task_propagate_pointer")]
323            #[allow(unused_unsafe)]
324            pub unsafe fn propagate(self) -> Result<V, glib::Error> {
325                let mut error = ptr::null_mut();
326
327                unsafe {
328                    #[cfg(feature = "v2_64")]
329                    {
330                        let mut value = glib::Value::uninitialized();
331                        ffi::g_task_propagate_value(
332                            self.to_glib_none().0,
333                            value.to_glib_none_mut().0,
334                            &mut error,
335                        );
336
337                        if error.is_null() {
338                            Ok(V::from_value(&value))
339                        } else {
340                            Err(from_glib_full(error))
341                        }
342                    }
343
344                    #[cfg(not(feature = "v2_64"))]
345                    {
346                        let value = ffi::g_task_propagate_pointer(self.to_glib_none().0, &mut error);
347
348                        if error.is_null() {
349                            let value = Option::<glib::Value>::from_glib_full(
350                                value as *mut glib::gobject_ffi::GValue,
351                            )
352                            .expect("Task::propagate() called before Task::return_result()");
353                            Ok(V::from_value(&value))
354                        } else {
355                            Err(from_glib_full(error))
356                        }
357                    }
358                }
359            }
360
361            // rustdoc-stripper-ignore-next
362            /// Gets the result of the task as a boolean, or the error
363            ///
364            /// # Safety
365            ///
366            /// This must only be called once, and only if the result was set
367            /// via [`Task::return_boolean_result`], or `g_task_return_boolean`.
368            #[doc(alias = "g_task_propagate_boolean")]
369            #[allow(unused_unsafe)]
370            pub unsafe fn propagate_boolean(self) -> Result<bool, glib::Error> {
371                let mut error = ptr::null_mut();
372
373                unsafe {
374                    let res = ffi::g_task_propagate_boolean(self.to_glib_none().0, &mut error);
375
376                    if error.is_null() {
377                        Ok(res != 0)
378                    } else {
379                        Err(from_glib_full(error))
380                    }
381                }
382            }
383
384            // rustdoc-stripper-ignore-next
385            /// Gets the result of the task as an int, or the error
386            ///
387            /// # Safety
388            ///
389            /// This must only be called once, and only if the result was set
390            /// via [`Task::return_int_result`], or `g_task_return_int`.
391            #[doc(alias = "g_task_propagate_int")]
392            #[allow(unused_unsafe)]
393            pub unsafe fn propagate_int(self) -> Result<isize, glib::Error> {
394                let mut error = ptr::null_mut();
395
396                unsafe {
397                    let res = ffi::g_task_propagate_int(self.to_glib_none().0, &mut error);
398
399                    if error.is_null() {
400                        Ok(res)
401                    } else {
402                        Err(from_glib_full(error))
403                    }
404                }
405            }
406        }
407    }
408}
409
410task_impl!(LocalTask);
411task_impl!(Task, @bound: Send, @safety: unsafe);
412
413impl<V: ValueType + Send> Task<V> {
414    #[doc(alias = "g_task_run_in_thread")]
415    pub fn run_in_thread<S, Q>(&self, task_func: Q)
416    where
417        S: IsA<glib::Object> + Send,
418        Q: FnOnce(Self, Option<&S>, Option<&Cancellable>) + Send + 'static,
419    {
420        let task_func_data = Box_::new(task_func);
421
422        // We store the func pointer into the task data.
423        // We intentionally do not expose a way to set the task data in the bindings.
424        // If we detect that the task data is set, there is not much we can do, so we panic.
425        unsafe {
426            assert!(
427                ffi::g_task_get_task_data(self.to_glib_none().0).is_null(),
428                "Task data was manually set or the task was run thread multiple times"
429            );
430
431            ffi::g_task_set_task_data(
432                self.to_glib_none().0,
433                Box_::into_raw(task_func_data) as *mut _,
434                None,
435            );
436        }
437
438        unsafe extern "C" fn trampoline<V, S, Q>(
439            task: *mut ffi::GTask,
440            source_object: *mut glib::gobject_ffi::GObject,
441            user_data: glib::ffi::gpointer,
442            cancellable: *mut ffi::GCancellable,
443        ) where
444            V: ValueType + Send,
445            S: IsA<glib::Object> + Send,
446            Q: FnOnce(Task<V>, Option<&S>, Option<&Cancellable>) + Send + 'static,
447        {
448            let task = Task::from_glib_none(task);
449            let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
450            let cancellable = Option::<Cancellable>::from_glib_borrow(cancellable);
451            let task_func: Box_<Q> = Box::from_raw(user_data as *mut _);
452            task_func(
453                task,
454                source_object.as_ref().as_ref().map(|s| s.unsafe_cast_ref()),
455                cancellable.as_ref().as_ref(),
456            );
457        }
458
459        let task_func = trampoline::<V, S, Q>;
460        unsafe {
461            ffi::g_task_run_in_thread(self.to_glib_none().0, Some(task_func));
462        }
463    }
464}
465
466unsafe impl<V: ValueType + Send> Send for Task<V> {}
467unsafe impl<V: ValueType + Send> Sync for Task<V> {}
468
469// rustdoc-stripper-ignore-next
470/// A handle to a task running on the I/O thread pool.
471///
472/// Like [`std::thread::JoinHandle`] for a blocking I/O task rather than a thread. The return value
473/// from the task can be retrieved by awaiting on this handle. Dropping the handle "detaches" the
474/// task, allowing it to complete but discarding the return value.
475#[derive(Debug)]
476pub struct JoinHandle<T> {
477    rx: oneshot::Receiver<std::thread::Result<T>>,
478}
479
480impl<T> JoinHandle<T> {
481    #[inline]
482    fn new() -> (Self, oneshot::Sender<std::thread::Result<T>>) {
483        let (tx, rx) = oneshot::channel();
484        (Self { rx }, tx)
485    }
486}
487
488impl<T> Future for JoinHandle<T> {
489    type Output = std::thread::Result<T>;
490    #[inline]
491    fn poll(
492        mut self: std::pin::Pin<&mut Self>,
493        cx: &mut std::task::Context<'_>,
494    ) -> std::task::Poll<Self::Output> {
495        std::pin::Pin::new(&mut self.rx)
496            .poll(cx)
497            .map(|r| r.unwrap())
498    }
499}
500
501impl<T> futures_core::FusedFuture for JoinHandle<T> {
502    #[inline]
503    fn is_terminated(&self) -> bool {
504        self.rx.is_terminated()
505    }
506}
507
508// rustdoc-stripper-ignore-next
509/// Runs a blocking I/O task on the I/O thread pool.
510///
511/// Calls `func` on the internal Gio thread pool for blocking I/O operations. The thread pool is
512/// shared with other Gio async I/O operations, and may rate-limit the tasks it receives. Callers
513/// may want to avoid blocking indefinitely by making sure blocking calls eventually time out.
514///
515/// This function should not be used to spawn async tasks. Instead, use
516/// [`glib::MainContext::spawn`] or [`glib::MainContext::spawn_local`] to run a future.
517pub fn spawn_blocking<T, F>(func: F) -> JoinHandle<T>
518where
519    T: Send + 'static,
520    F: FnOnce() -> T + Send + 'static,
521{
522    // use Cancellable::NONE as source obj to fulfill `Send` requirement
523    let task = unsafe { Task::<bool>::new(Cancellable::NONE, Cancellable::NONE, |_, _| {}) };
524    let (join, tx) = JoinHandle::new();
525    task.run_in_thread(move |task, _: Option<&Cancellable>, _| {
526        let res = panic::catch_unwind(panic::AssertUnwindSafe(func));
527        let _ = tx.send(res);
528        unsafe { ffi::g_task_return_pointer(task.to_glib_none().0, ptr::null_mut(), None) }
529    });
530
531    join
532}
533
534#[cfg(test)]
535mod test {
536    use super::*;
537    use crate::{prelude::*, test_util::run_async_local};
538
539    #[test]
540    fn test_int_value_async_result() {
541        let fut = run_async_local(|tx, l| {
542            let cancellable = crate::Cancellable::new();
543            let task = unsafe {
544                crate::LocalTask::new(
545                    None,
546                    Some(&cancellable),
547                    move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
548                        tx.send(t.propagate()).unwrap();
549                        l.quit();
550                    },
551                )
552            };
553            task.return_result(Ok(100_i32));
554        });
555
556        match fut {
557            Err(_) => panic!(),
558            Ok(i) => assert_eq!(i, 100),
559        }
560    }
561
562    #[test]
563    fn test_boolean_async_result() {
564        let fut = run_async_local(|tx, l| {
565            let cancellable = crate::Cancellable::new();
566            let task = unsafe {
567                crate::LocalTask::new(
568                    None,
569                    Some(&cancellable),
570                    move |t: LocalTask<bool>, _b: Option<&glib::Object>| {
571                        tx.send(t.propagate_boolean()).unwrap();
572                        l.quit();
573                    },
574                )
575            };
576            task.return_boolean_result(Ok(true));
577        });
578
579        match fut {
580            Err(_) => panic!(),
581            Ok(i) => assert!(i),
582        }
583    }
584
585    #[test]
586    fn test_int_async_result() {
587        let fut = run_async_local(|tx, l| {
588            let cancellable = crate::Cancellable::new();
589            let task = unsafe {
590                crate::LocalTask::new(
591                    None,
592                    Some(&cancellable),
593                    move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
594                        tx.send(t.propagate_int()).unwrap();
595                        l.quit();
596                    },
597                )
598            };
599            task.return_int_result(Ok(100_isize));
600        });
601
602        match fut {
603            Err(_) => panic!(),
604            Ok(i) => assert_eq!(i, 100),
605        }
606    }
607
608    #[test]
609    fn test_object_async_result() {
610        use glib::subclass::prelude::*;
611        pub struct MySimpleObjectPrivate {
612            pub size: std::cell::RefCell<Option<i64>>,
613        }
614
615        #[glib::object_subclass]
616        impl ObjectSubclass for MySimpleObjectPrivate {
617            const NAME: &'static str = "MySimpleObjectPrivate";
618            type Type = MySimpleObject;
619
620            fn new() -> Self {
621                Self {
622                    size: std::cell::RefCell::new(Some(100)),
623                }
624            }
625        }
626
627        impl ObjectImpl for MySimpleObjectPrivate {}
628
629        glib::wrapper! {
630            pub struct MySimpleObject(ObjectSubclass<MySimpleObjectPrivate>);
631        }
632
633        impl MySimpleObject {
634            pub fn new() -> Self {
635                glib::Object::new()
636            }
637
638            #[doc(alias = "get_size")]
639            pub fn size(&self) -> Option<i64> {
640                *self.imp().size.borrow()
641            }
642
643            pub fn set_size(&self, size: i64) {
644                self.imp().size.borrow_mut().replace(size);
645            }
646        }
647
648        impl Default for MySimpleObject {
649            fn default() -> Self {
650                Self::new()
651            }
652        }
653
654        let fut = run_async_local(|tx, l| {
655            let cancellable = crate::Cancellable::new();
656            let task = unsafe {
657                crate::LocalTask::new(
658                    None,
659                    Some(&cancellable),
660                    move |t: LocalTask<glib::Object>, _b: Option<&glib::Object>| {
661                        tx.send(t.propagate()).unwrap();
662                        l.quit();
663                    },
664                )
665            };
666            let my_object = MySimpleObject::new();
667            my_object.set_size(100);
668            task.return_result(Ok(my_object.upcast::<glib::Object>()));
669        });
670
671        match fut {
672            Err(_) => panic!(),
673            Ok(o) => {
674                let o = o.downcast::<MySimpleObject>().unwrap();
675                assert_eq!(o.size(), Some(100));
676            }
677        }
678    }
679
680    #[test]
681    fn test_error() {
682        let fut = run_async_local(|tx, l| {
683            let cancellable = crate::Cancellable::new();
684            let task = unsafe {
685                crate::LocalTask::new(
686                    None,
687                    Some(&cancellable),
688                    move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
689                        tx.send(t.propagate()).unwrap();
690                        l.quit();
691                    },
692                )
693            };
694            task.return_result(Err(glib::Error::new(
695                crate::IOErrorEnum::WouldBlock,
696                "WouldBlock",
697            )));
698        });
699
700        match fut {
701            Err(e) => match e.kind().unwrap() {
702                crate::IOErrorEnum::WouldBlock => {}
703                _ => panic!(),
704            },
705            Ok(_) => panic!(),
706        }
707    }
708
709    #[test]
710    fn test_cancelled() {
711        let fut = run_async_local(|tx, l| {
712            let cancellable = crate::Cancellable::new();
713            let task = unsafe {
714                crate::LocalTask::new(
715                    None,
716                    Some(&cancellable),
717                    move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
718                        tx.send(t.propagate()).unwrap();
719                        l.quit();
720                    },
721                )
722            };
723            cancellable.cancel();
724            task.return_error_if_cancelled();
725        });
726
727        match fut {
728            Err(e) => match e.kind().unwrap() {
729                crate::IOErrorEnum::Cancelled => {}
730                _ => panic!(),
731            },
732            Ok(_) => panic!(),
733        }
734    }
735}