Skip to main content

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::{SignalHandlerId, connect_raw},
8    translate::*,
9};
10
11use futures_channel::oneshot;
12
13use crate::{AsyncResult, Cancellable, ffi};
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    // rustdoc-stripper-ignore-next-stop
54    /// error);
55    ///   if (error)
56    ///     {
57    ///       g_task_return_error (task, error);
58    ///       return;
59    ///     }
60    ///
61    ///   // If the task has already been cancelled, then we don’t want to add
62    ///   // the cake to the cake cache. Likewise, we don’t  want to have the
63    ///   // task get cancelled in the middle of updating the cache.
64    ///   // g_task_set_return_on_cancel() will return [`true`] here if it managed
65    ///   // to disable return-on-cancel, or [`false`] if the task was cancelled
66    ///   // before it could.
67    ///   if (g_task_set_return_on_cancel (task, FALSE))
68    ///     {
69    ///       // If the caller cancels at this point, their
70    ///       // GAsyncReadyCallback won’t be invoked until we return,
71    ///       // so we don’t have to worry that this code will run at
72    ///       // the same time as that code does. But if there were
73    ///       // other functions that might look at the cake cache,
74    ///       // then we’d probably need a GMutex here as well.
75    ///       baker_add_cake_to_cache (baker, cake);
76    ///       g_task_return_pointer (task, cake, g_object_unref);
77    ///     }
78    /// }
79    ///
80    /// void
81    /// baker_bake_cake_async (Baker               *self,
82    ///                        guint                radius,
83    ///                        CakeFlavor           flavor,
84    ///                        CakeFrostingType     frosting,
85    ///                        const char          *message,
86    ///                        GCancellable        *cancellable,
87    ///                        GAsyncReadyCallback  callback,
88    ///                        gpointer             user_data)
89    /// {
90    ///   CakeData *cake_data;
91    ///   GTask *task;
92    ///
93    ///   cake_data = g_slice_new (CakeData);
94    ///
95    ///   ...
96    ///
97    ///   task = g_task_new (self, cancellable, callback, user_data);
98    ///   g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
99    ///   g_task_set_return_on_cancel (task, TRUE);
100    ///   g_task_run_in_thread (task, bake_cake_thread);
101    /// }
102    ///
103    /// Cake *
104    /// baker_bake_cake_sync (Baker               *self,
105    ///                       guint                radius,
106    ///                       CakeFlavor           flavor,
107    ///                       CakeFrostingType     frosting,
108    ///                       const char          *message,
109    ///                       GCancellable        *cancellable,
110    ///                       GError             **error)
111    /// {
112    ///   CakeData *cake_data;
113    ///   GTask *task;
114    ///   Cake *cake;
115    ///
116    ///   cake_data = g_slice_new (CakeData);
117    ///
118    ///   ...
119    ///
120    ///   task = g_task_new (self, cancellable, NULL, NULL);
121    ///   g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
122    ///   g_task_set_return_on_cancel (task, TRUE);
123    ///   g_task_run_in_thread_sync (task, bake_cake_thread);
124    ///
125    ///   cake = g_task_propagate_pointer (task, error);
126    ///   g_object_unref (task);
127    ///   return cake;
128    /// }
129    /// ```text
130    ///
131    /// ## Porting from `Gio::SimpleAsyncResult`
132    ///
133    /// `GTask`’s API attempts to be simpler than `Gio::SimpleAsyncResult`’s
134    /// in several ways:
135    ///
136    /// - You can save task-specific data with [`set_task_data()`][Self::set_task_data()], and
137    ///   retrieve it later with [`task_data()`][Self::task_data()]. This replaces the
138    ///   abuse of `Gio::SimpleAsyncResult::set_op_res_gpointer()` for the same
139    ///   purpose with `Gio::SimpleAsyncResult`.
140    /// - In addition to the task data, `GTask` also keeps track of the
141    ///   [priority](iface.AsyncResult.html#io-priority), [`Cancellable`][crate::Cancellable],
142    ///   and [`glib::MainContext`][crate::glib::MainContext] associated with the task, so tasks that
143    ///   consist of a chain of simpler asynchronous operations will have easy access
144    ///   to those values when starting each sub-task.
145    /// - [`return_error_if_cancelled()`][Self::return_error_if_cancelled()] provides simplified
146    ///   handling for cancellation. In addition, cancellation
147    ///   overrides any other `GTask` return value by default, like
148    ///   `Gio::SimpleAsyncResult` does when
149    ///   `Gio::SimpleAsyncResult::set_check_cancellable()` is called.
150    ///   (You can use [`set_check_cancellable()`][Self::set_check_cancellable()] to turn off that
151    ///   behavior.) On the other hand, [`run_in_thread()`][Self::run_in_thread()]
152    ///   guarantees that it will always run your
153    ///   `task_func`, even if the task’s [`Cancellable`][crate::Cancellable]
154    ///   is already cancelled before the task gets a chance to run;
155    ///   you can start your `task_func` with a
156    ///   [`return_error_if_cancelled()`][Self::return_error_if_cancelled()] check if you need the
157    ///   old behavior.
158    /// - The ‘return’ methods (eg, [`return_pointer()`][Self::return_pointer()])
159    ///   automatically cause the task to be ‘completed’ as well, and
160    ///   there is no need to worry about the ‘complete’ vs ‘complete in idle’
161    ///   distinction. (`GTask` automatically figures out
162    ///   whether the task’s callback can be invoked directly, or
163    ///   if it needs to be sent to another [`glib::MainContext`][crate::glib::MainContext], or delayed
164    ///   until the next iteration of the current [`glib::MainContext`][crate::glib::MainContext].)
165    /// - The ‘finish’ functions for `GTask` based operations are generally
166    ///   much simpler than `Gio::SimpleAsyncResult` ones, normally consisting
167    ///   of only a single call to [`propagate_pointer()`][Self::propagate_pointer()] or the like.
168    ///   Since [`propagate_pointer()`][Self::propagate_pointer()] ‘steals’ the return value from
169    ///   the `GTask`, it is not necessary to juggle pointers around to
170    ///   prevent it from being freed twice.
171    /// - With `Gio::SimpleAsyncResult`, it was common to call
172    ///   `Gio::SimpleAsyncResult::propagate_error()` from the
173    ///   `_finish()` wrapper function, and have
174    ///   virtual method implementations only deal with successful
175    ///   returns. This behavior is deprecated, because it makes it
176    ///   difficult for a subclass to chain to a parent class’s async
177    ///   methods. Instead, the wrapper function should just be a
178    ///   simple wrapper, and the virtual method should call an
179    ///   appropriate `g_task_propagate_` function.
180    ///   Note that wrapper methods can now use
181    ///   [`AsyncResultExt::legacy_propagate_error()`][crate::prelude::AsyncResultExt::legacy_propagate_error()] to do old-style
182    ///   `Gio::SimpleAsyncResult` error-returning behavior, and
183    ///   `Gio::AsyncResult::is_tagged()` to check if a result is tagged as
184    ///   having come from the `_async()` wrapper
185    ///   function (for ‘short-circuit’ results, such as when passing
186    ///   `0` to [`InputStreamExtManual::read_async()`][crate::prelude::InputStreamExtManual::read_async()]).
187    ///
188    /// ## Thread-safety considerations
189    ///
190    /// Due to some infelicities in the API design, there is a
191    /// thread-safety concern that users of `GTask` have to be aware of:
192    ///
193    /// If the `main` thread drops its last reference to the source object
194    /// or the task data before the task is finalized, then the finalizers
195    /// of these objects may be called on the worker thread.
196    ///
197    /// This is a problem if the finalizers use non-threadsafe API, and
198    /// can lead to hard-to-debug crashes. Possible workarounds include:
199    ///
200    /// - Clear task data in a signal handler for `notify::completed`
201    /// - Keep iterating a main context in the main thread and defer
202    ///   dropping the reference to the source object to that main
203    ///   context when the task is finalized
204    ///
205    /// ## Properties
206    ///
207    ///
208    /// #### `completed`
209    ///  Whether the task has completed, meaning its callback (if set) has been
210    /// invoked.
211    ///
212    /// This can only happen after g_task_return_pointer(),
213    /// g_task_return_error() or one of the other return functions have been called
214    /// on the task. However, it is not guaranteed to happen immediately after
215    /// those functions are called, as the task’s callback may need to be scheduled
216    /// to run in a different thread.
217    ///
218    /// That means it is **not safe** to use this property to track whether a
219    /// return function has been called on the #GTask. Callers must do that
220    /// tracking themselves, typically by linking the lifetime of the #GTask to the
221    /// control flow of their code.
222    ///
223    /// This property is guaranteed to change from [`false`] to [`true`] exactly once.
224    ///
225    /// The #GObject::notify signal for this change is emitted in the same main
226    /// context as the task’s callback, immediately after that callback is invoked.
227    ///
228    /// Readable
229    ///
230    /// # Implements
231    ///
232    /// [`trait@glib::ObjectExt`], [`AsyncResultExt`][trait@crate::prelude::AsyncResultExt]
233    #[doc(alias = "GTask")]
234    pub struct Task<V: ValueType + Send>(Object<ffi::GTask, ffi::GTaskClass>) @implements AsyncResult;
235
236    match fn {
237        type_ => || ffi::g_task_get_type(),
238    }
239}
240
241macro_rules! task_impl {
242    ($name:ident $(, @bound: $bound:tt)? $(, @safety: $safety:tt)?) => {
243        impl <V: Into<glib::Value> + ValueType $(+ $bound)?> $name<V> {
244            #[doc(alias = "g_task_new")]
245            #[allow(unused_unsafe)]
246            pub unsafe fn new<S, P, Q>(
247                source_object: Option<&S>,
248                cancellable: Option<&P>,
249                callback: Q,
250            ) -> Self
251            where
252                S: IsA<glib::Object> $(+ $bound)?,
253                P: IsA<Cancellable>,
254                Q: FnOnce($name<V>, Option<&S>) $(+ $bound)? + 'static,
255            {
256                let callback_data = Box_::new(callback);
257                unsafe extern "C" fn trampoline<
258                    S: IsA<glib::Object> $(+ $bound)?,
259                    V: ValueType $(+ $bound)?,
260                    Q: FnOnce($name<V>, Option<&S>) $(+ $bound)? + 'static,
261                >(
262                    source_object: *mut glib::gobject_ffi::GObject,
263                    res: *mut ffi::GAsyncResult,
264                    user_data: glib::ffi::gpointer,
265                ) { unsafe {
266                    let callback: Box_<Q> = Box::from_raw(user_data as *mut _);
267                    let task = AsyncResult::from_glib_none(res)
268                        .downcast::<$name<V>>()
269                        .unwrap();
270                    let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
271                    callback(
272                        task,
273                        source_object.as_ref().as_ref().map(|s| s.unsafe_cast_ref()),
274                    );
275                }}
276                let callback = trampoline::<S, V, Q>;
277                unsafe {
278                    from_glib_full(ffi::g_task_new(
279                        source_object.map(|p| p.as_ref()).to_glib_none().0,
280                        cancellable.map(|p| p.as_ref()).to_glib_none().0,
281                        Some(callback),
282                        Box_::into_raw(callback_data) as *mut _,
283                    ))
284                }
285            }
286
287            #[doc(alias = "g_task_get_cancellable")]
288            #[doc(alias = "get_cancellable")]
289            pub fn cancellable(&self) -> Option<Cancellable> {
290                unsafe { from_glib_none(ffi::g_task_get_cancellable(self.to_glib_none().0)) }
291            }
292
293            #[doc(alias = "g_task_get_check_cancellable")]
294            #[doc(alias = "get_check_cancellable")]
295            pub fn is_check_cancellable(&self) -> bool {
296                unsafe { from_glib(ffi::g_task_get_check_cancellable(self.to_glib_none().0)) }
297            }
298
299            #[doc(alias = "g_task_set_check_cancellable")]
300            pub fn set_check_cancellable(&self, check_cancellable: bool) {
301                unsafe {
302                    ffi::g_task_set_check_cancellable(self.to_glib_none().0, check_cancellable.into_glib());
303                }
304            }
305
306            #[cfg(feature = "v2_60")]
307            #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
308            #[doc(alias = "g_task_set_name")]
309            pub fn set_name(&self, name: Option<&str>) {
310                unsafe {
311                    ffi::g_task_set_name(self.to_glib_none().0, name.to_glib_none().0);
312                }
313            }
314
315            #[doc(alias = "g_task_set_return_on_cancel")]
316            pub fn set_return_on_cancel(&self, return_on_cancel: bool) -> bool {
317                unsafe {
318                    from_glib(ffi::g_task_set_return_on_cancel(
319                        self.to_glib_none().0,
320                        return_on_cancel.into_glib(),
321                    ))
322                }
323            }
324
325            #[doc(alias = "g_task_is_valid")]
326            pub fn is_valid(
327                result: &impl IsA<AsyncResult>,
328                source_object: Option<&impl IsA<glib::Object>>,
329            ) -> bool {
330                unsafe {
331                    from_glib(ffi::g_task_is_valid(
332                        result.as_ref().to_glib_none().0,
333                        source_object.map(|p| p.as_ref()).to_glib_none().0,
334                    ))
335                }
336            }
337
338            #[doc(alias = "get_priority")]
339            #[doc(alias = "g_task_get_priority")]
340            pub fn priority(&self) -> glib::source::Priority {
341                unsafe { FromGlib::from_glib(ffi::g_task_get_priority(self.to_glib_none().0)) }
342            }
343
344            #[doc(alias = "g_task_set_priority")]
345            pub fn set_priority(&self, priority: glib::source::Priority) {
346                unsafe {
347                    ffi::g_task_set_priority(self.to_glib_none().0, priority.into_glib());
348                }
349            }
350
351            #[doc(alias = "g_task_get_completed")]
352            #[doc(alias = "get_completed")]
353            pub fn is_completed(&self) -> bool {
354                unsafe { from_glib(ffi::g_task_get_completed(self.to_glib_none().0)) }
355            }
356
357            #[doc(alias = "g_task_get_context")]
358            #[doc(alias = "get_context")]
359            pub fn context(&self) -> glib::MainContext {
360                unsafe { from_glib_none(ffi::g_task_get_context(self.to_glib_none().0)) }
361            }
362
363            #[cfg(feature = "v2_60")]
364            #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
365            #[doc(alias = "g_task_get_name")]
366            #[doc(alias = "get_name")]
367            pub fn name(&self) -> Option<glib::GString> {
368                unsafe { from_glib_none(ffi::g_task_get_name(self.to_glib_none().0)) }
369            }
370
371            #[doc(alias = "g_task_get_return_on_cancel")]
372            #[doc(alias = "get_return_on_cancel")]
373            pub fn is_return_on_cancel(&self) -> bool {
374                unsafe { from_glib(ffi::g_task_get_return_on_cancel(self.to_glib_none().0)) }
375            }
376
377            #[doc(alias = "g_task_had_error")]
378            pub fn had_error(&self) -> bool {
379                unsafe { from_glib(ffi::g_task_had_error(self.to_glib_none().0)) }
380            }
381
382            #[doc(alias = "completed")]
383            pub fn connect_completed_notify<F>(&self, f: F) -> SignalHandlerId
384            where
385                F: Fn(&$name<V>) $(+ $bound)? + 'static,
386            {
387                unsafe extern "C" fn notify_completed_trampoline<V, F>(
388                    this: *mut ffi::GTask,
389                    _param_spec: glib::ffi::gpointer,
390                    f: glib::ffi::gpointer,
391                ) where
392                    V: ValueType $(+ $bound)?,
393                    F: Fn(&$name<V>) + 'static,
394                { unsafe {
395                    let f: &F = &*(f as *const F);
396                    f(&from_glib_borrow(this))
397                }}
398                unsafe {
399                    let f: Box_<F> = Box_::new(f);
400                    connect_raw(
401                        self.as_ptr() as *mut _,
402                        b"notify::completed\0".as_ptr() as *const _,
403                        Some(transmute::<*const (), unsafe extern "C" fn()>(
404                            notify_completed_trampoline::<V, F> as *const (),
405                        )),
406                        Box_::into_raw(f),
407                    )
408                }
409            }
410
411            // the following functions are marked unsafe since they cannot be called
412            // more than once, but we have no way to enforce that since the task can be cloned
413
414            #[doc(alias = "g_task_return_error_if_cancelled")]
415            #[allow(unused_unsafe)]
416            pub $($safety)? fn return_error_if_cancelled(&self) -> bool {
417                unsafe { from_glib(ffi::g_task_return_error_if_cancelled(self.to_glib_none().0)) }
418            }
419
420            // rustdoc-stripper-ignore-next
421            /// Set the result of the task
422            ///
423            /// # Safety
424            ///
425            /// The value must be read with [`Task::propagate`],
426            /// `g_task_propagate_value` or `g_task_propagate_pointer`.
427            #[doc(alias = "g_task_return_value")]
428            #[doc(alias = "g_task_return_pointer")]
429            #[doc(alias = "g_task_return_error")]
430            #[allow(unused_unsafe)]
431            pub $($safety)? fn return_result(self, result: Result<V, glib::Error>) {
432                #[cfg(not(feature = "v2_64"))]
433                unsafe extern "C" fn value_free(value: *mut libc::c_void) { unsafe {
434                    let _: glib::Value = from_glib_full(value as *mut glib::gobject_ffi::GValue);
435                }}
436
437                match result {
438                    #[cfg(feature = "v2_64")]
439                    Ok(v) => unsafe {
440                        ffi::g_task_return_value(
441                            self.to_glib_none().0,
442                            v.to_value().to_glib_none().0 as *mut _,
443                        )
444                    },
445                    #[cfg(not(feature = "v2_64"))]
446                    Ok(v) => unsafe {
447                        let v: glib::Value = v.into();
448                        ffi::g_task_return_pointer(
449                            self.to_glib_none().0,
450                            <glib::Value as glib::translate::IntoGlibPtr::<*mut glib::gobject_ffi::GValue>>::into_glib_ptr(v) as glib::ffi::gpointer,
451                            Some(value_free),
452                        )
453                    },
454                    Err(e) => unsafe {
455                        ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr());
456                    },
457                }
458            }
459
460            // rustdoc-stripper-ignore-next
461            /// Set the result of the task as a boolean
462            ///
463            /// # Safety
464            ///
465            /// The value must be read with [`Task::propagate_boolean`],
466            /// or `g_task_propagate_boolean`.
467            #[doc(alias = "g_task_return_boolean")]
468            #[allow(unused_unsafe)]
469            pub $($safety)? fn return_boolean_result(self, result: Result<bool, glib::Error>) {
470                match result {
471                    Ok(v) =>  unsafe { ffi::g_task_return_boolean(self.to_glib_none().0, v as i32) },
472                    Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) },
473                }
474            }
475
476            // rustdoc-stripper-ignore-next
477            /// Set the result of the task as an int
478            ///
479            /// # Safety
480            ///
481            /// The value must be read with [`Task::propagate_int`],
482            /// or `g_task_propagate_int`.
483            #[doc(alias = "g_task_return_int")]
484            #[allow(unused_unsafe)]
485            pub $($safety)? fn return_int_result(self, result: Result<isize, glib::Error>) {
486                match result {
487                    Ok(v) =>  unsafe { ffi::g_task_return_int(self.to_glib_none().0, v) },
488                    Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) },
489                }
490            }
491
492
493            // rustdoc-stripper-ignore-next
494            /// Gets the result of the task and transfers ownership of it
495            ///
496            /// # Safety
497            ///
498            /// This must only be called once, and only if the result was set
499            /// via [`Task::return_result`], `g_task_return_value` or
500            /// `g_task_return_pointer`.
501            #[doc(alias = "g_task_propagate_value")]
502            #[doc(alias = "g_task_propagate_pointer")]
503            #[allow(unused_unsafe)]
504            pub unsafe fn propagate(self) -> Result<V, glib::Error> {
505                let mut error = ptr::null_mut();
506
507                unsafe {
508                    #[cfg(feature = "v2_64")]
509                    {
510                        let mut value = glib::Value::uninitialized();
511                        ffi::g_task_propagate_value(
512                            self.to_glib_none().0,
513                            value.to_glib_none_mut().0,
514                            &mut error,
515                        );
516
517                        if error.is_null() {
518                            Ok(V::from_value(&value))
519                        } else {
520                            Err(from_glib_full(error))
521                        }
522                    }
523
524                    #[cfg(not(feature = "v2_64"))]
525                    {
526                        let value = ffi::g_task_propagate_pointer(self.to_glib_none().0, &mut error);
527
528                        if error.is_null() {
529                            let value = Option::<glib::Value>::from_glib_full(
530                                value as *mut glib::gobject_ffi::GValue,
531                            )
532                            .expect("Task::propagate() called before Task::return_result()");
533                            Ok(V::from_value(&value))
534                        } else {
535                            Err(from_glib_full(error))
536                        }
537                    }
538                }
539            }
540
541            // rustdoc-stripper-ignore-next
542            /// Gets the result of the task as a boolean, or the error
543            ///
544            /// # Safety
545            ///
546            /// This must only be called once, and only if the result was set
547            /// via [`Task::return_boolean_result`], or `g_task_return_boolean`.
548            #[doc(alias = "g_task_propagate_boolean")]
549            #[allow(unused_unsafe)]
550            pub unsafe fn propagate_boolean(self) -> Result<bool, glib::Error> {
551                let mut error = ptr::null_mut();
552
553                unsafe {
554                    let res = ffi::g_task_propagate_boolean(self.to_glib_none().0, &mut error);
555
556                    if error.is_null() {
557                        Ok(res != 0)
558                    } else {
559                        Err(from_glib_full(error))
560                    }
561                }
562            }
563
564            // rustdoc-stripper-ignore-next
565            /// Gets the result of the task as an int, or the error
566            ///
567            /// # Safety
568            ///
569            /// This must only be called once, and only if the result was set
570            /// via [`Task::return_int_result`], or `g_task_return_int`.
571            #[doc(alias = "g_task_propagate_int")]
572            #[allow(unused_unsafe)]
573            pub unsafe fn propagate_int(self) -> Result<isize, glib::Error> {
574                let mut error = ptr::null_mut();
575
576                unsafe {
577                    let res = ffi::g_task_propagate_int(self.to_glib_none().0, &mut error);
578
579                    if error.is_null() {
580                        Ok(res)
581                    } else {
582                        Err(from_glib_full(error))
583                    }
584                }
585            }
586        }
587    }
588}
589
590task_impl!(LocalTask);
591task_impl!(Task, @bound: Send, @safety: unsafe);
592
593impl<V: ValueType + Send> Task<V> {
594    #[doc(alias = "g_task_run_in_thread")]
595    pub fn run_in_thread<S, Q>(&self, task_func: Q)
596    where
597        S: IsA<glib::Object> + Send,
598        Q: FnOnce(Self, Option<&S>, Option<&Cancellable>) + Send + 'static,
599    {
600        let task_func_data = Box_::new(task_func);
601
602        // We store the func pointer into the task data.
603        // We intentionally do not expose a way to set the task data in the bindings.
604        // If we detect that the task data is set, there is not much we can do, so we panic.
605        unsafe {
606            assert!(
607                ffi::g_task_get_task_data(self.to_glib_none().0).is_null(),
608                "Task data was manually set or the task was run thread multiple times"
609            );
610
611            ffi::g_task_set_task_data(
612                self.to_glib_none().0,
613                Box_::into_raw(task_func_data) as *mut _,
614                None,
615            );
616        }
617
618        unsafe extern "C" fn trampoline<V, S, Q>(
619            task: *mut ffi::GTask,
620            source_object: *mut glib::gobject_ffi::GObject,
621            user_data: glib::ffi::gpointer,
622            cancellable: *mut ffi::GCancellable,
623        ) where
624            V: ValueType + Send,
625            S: IsA<glib::Object> + Send,
626            Q: FnOnce(Task<V>, Option<&S>, Option<&Cancellable>) + Send + 'static,
627        {
628            unsafe {
629                let task = Task::from_glib_none(task);
630                let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
631                let cancellable = Option::<Cancellable>::from_glib_borrow(cancellable);
632                let task_func: Box_<Q> = Box::from_raw(user_data as *mut _);
633                task_func(
634                    task,
635                    source_object.as_ref().as_ref().map(|s| s.unsafe_cast_ref()),
636                    cancellable.as_ref().as_ref(),
637                );
638            }
639        }
640
641        let task_func = trampoline::<V, S, Q>;
642        unsafe {
643            ffi::g_task_run_in_thread(self.to_glib_none().0, Some(task_func));
644        }
645    }
646}
647
648unsafe impl<V: ValueType + Send> Send for Task<V> {}
649unsafe impl<V: ValueType + Send> Sync for Task<V> {}
650
651// rustdoc-stripper-ignore-next
652/// A handle to a task running on the I/O thread pool.
653///
654/// Like [`std::thread::JoinHandle`] for a blocking I/O task rather than a thread. The return value
655/// from the task can be retrieved by awaiting on this handle. Dropping the handle "detaches" the
656/// task, allowing it to complete but discarding the return value.
657#[derive(Debug)]
658pub struct JoinHandle<T> {
659    rx: oneshot::Receiver<std::thread::Result<T>>,
660}
661
662impl<T> JoinHandle<T> {
663    #[inline]
664    fn new() -> (Self, oneshot::Sender<std::thread::Result<T>>) {
665        let (tx, rx) = oneshot::channel();
666        (Self { rx }, tx)
667    }
668}
669
670impl<T> Future for JoinHandle<T> {
671    type Output = std::thread::Result<T>;
672    #[inline]
673    fn poll(
674        mut self: std::pin::Pin<&mut Self>,
675        cx: &mut std::task::Context<'_>,
676    ) -> std::task::Poll<Self::Output> {
677        std::pin::Pin::new(&mut self.rx)
678            .poll(cx)
679            .map(|r| r.unwrap())
680    }
681}
682
683impl<T> futures_core::FusedFuture for JoinHandle<T> {
684    #[inline]
685    fn is_terminated(&self) -> bool {
686        self.rx.is_terminated()
687    }
688}
689
690// rustdoc-stripper-ignore-next
691/// Runs a blocking I/O task on the I/O thread pool.
692///
693/// Calls `func` on the internal Gio thread pool for blocking I/O operations. The thread pool is
694/// shared with other Gio async I/O operations, and may rate-limit the tasks it receives. Callers
695/// may want to avoid blocking indefinitely by making sure blocking calls eventually time out.
696///
697/// This function should not be used to spawn async tasks. Instead, use
698/// [`glib::MainContext::spawn`] or [`glib::MainContext::spawn_local`] to run a future.
699pub fn spawn_blocking<T, F>(func: F) -> JoinHandle<T>
700where
701    T: Send + 'static,
702    F: FnOnce() -> T + Send + 'static,
703{
704    unsafe extern "C" fn free_box<T: Send + 'static>(ptr: glib::ffi::gpointer) {
705        unsafe {
706            let _ = Box::from_raw(ptr as *mut std::thread::Result<T>);
707        }
708    }
709
710    let (join, tx) = JoinHandle::new();
711
712    // use Cancellable::NONE as source obj to fulfill `Send` requirement
713    let task = unsafe {
714        Task::<bool>::new(Cancellable::NONE, Cancellable::NONE, move |task, _| {
715            let mut err = ptr::null_mut();
716            let ptr = ffi::g_task_propagate_pointer(task.to_glib_none().0, &mut err);
717
718            let res = *Box::from_raw(ptr as *mut std::thread::Result<T>);
719            let _ = tx.send(res);
720        })
721    };
722    task.run_in_thread(move |task, _: Option<&Cancellable>, _| {
723        let res = panic::catch_unwind(panic::AssertUnwindSafe(func));
724        let tx = Box::new(res);
725
726        unsafe {
727            ffi::g_task_return_pointer(
728                task.to_glib_none().0,
729                Box::into_raw(tx) as glib::ffi::gpointer,
730                Some(free_box::<T>),
731            )
732        }
733    });
734
735    join
736}
737
738#[cfg(test)]
739mod test {
740    use super::*;
741    use crate::{prelude::*, test_util::run_async_local};
742
743    #[test]
744    fn test_int_value_async_result() {
745        let fut = run_async_local(|tx, l| {
746            let cancellable = crate::Cancellable::new();
747            let task = unsafe {
748                crate::LocalTask::new(
749                    None,
750                    Some(&cancellable),
751                    move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
752                        tx.send(t.propagate()).unwrap();
753                        l.quit();
754                    },
755                )
756            };
757            task.return_result(Ok(100_i32));
758        });
759
760        match fut {
761            Err(_) => panic!(),
762            Ok(i) => assert_eq!(i, 100),
763        }
764    }
765
766    #[test]
767    fn test_boolean_async_result() {
768        let fut = run_async_local(|tx, l| {
769            let cancellable = crate::Cancellable::new();
770            let task = unsafe {
771                crate::LocalTask::new(
772                    None,
773                    Some(&cancellable),
774                    move |t: LocalTask<bool>, _b: Option<&glib::Object>| {
775                        tx.send(t.propagate_boolean()).unwrap();
776                        l.quit();
777                    },
778                )
779            };
780            task.return_boolean_result(Ok(true));
781        });
782
783        match fut {
784            Err(_) => panic!(),
785            Ok(i) => assert!(i),
786        }
787    }
788
789    #[test]
790    fn test_int_async_result() {
791        let fut = run_async_local(|tx, l| {
792            let cancellable = crate::Cancellable::new();
793            let task = unsafe {
794                crate::LocalTask::new(
795                    None,
796                    Some(&cancellable),
797                    move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
798                        tx.send(t.propagate_int()).unwrap();
799                        l.quit();
800                    },
801                )
802            };
803            task.return_int_result(Ok(100_isize));
804        });
805
806        match fut {
807            Err(_) => panic!(),
808            Ok(i) => assert_eq!(i, 100),
809        }
810    }
811
812    #[test]
813    fn test_object_async_result() {
814        use glib::subclass::prelude::*;
815        pub struct MySimpleObjectPrivate {
816            pub size: std::cell::RefCell<Option<i64>>,
817        }
818
819        #[glib::object_subclass]
820        impl ObjectSubclass for MySimpleObjectPrivate {
821            const NAME: &'static str = "MySimpleObjectPrivate";
822            type Type = MySimpleObject;
823
824            fn new() -> Self {
825                Self {
826                    size: std::cell::RefCell::new(Some(100)),
827                }
828            }
829        }
830
831        impl ObjectImpl for MySimpleObjectPrivate {}
832
833        glib::wrapper! {
834            pub struct MySimpleObject(ObjectSubclass<MySimpleObjectPrivate>);
835        }
836
837        impl MySimpleObject {
838            pub fn new() -> Self {
839                glib::Object::new()
840            }
841
842            #[doc(alias = "get_size")]
843            pub fn size(&self) -> Option<i64> {
844                *self.imp().size.borrow()
845            }
846
847            pub fn set_size(&self, size: i64) {
848                self.imp().size.borrow_mut().replace(size);
849            }
850        }
851
852        impl Default for MySimpleObject {
853            fn default() -> Self {
854                Self::new()
855            }
856        }
857
858        let fut = run_async_local(|tx, l| {
859            let cancellable = crate::Cancellable::new();
860            let task = unsafe {
861                crate::LocalTask::new(
862                    None,
863                    Some(&cancellable),
864                    move |t: LocalTask<glib::Object>, _b: Option<&glib::Object>| {
865                        tx.send(t.propagate()).unwrap();
866                        l.quit();
867                    },
868                )
869            };
870            let my_object = MySimpleObject::new();
871            my_object.set_size(100);
872            task.return_result(Ok(my_object.upcast::<glib::Object>()));
873        });
874
875        match fut {
876            Err(_) => panic!(),
877            Ok(o) => {
878                let o = o.downcast::<MySimpleObject>().unwrap();
879                assert_eq!(o.size(), Some(100));
880            }
881        }
882    }
883
884    #[test]
885    fn test_error() {
886        let fut = run_async_local(|tx, l| {
887            let cancellable = crate::Cancellable::new();
888            let task = unsafe {
889                crate::LocalTask::new(
890                    None,
891                    Some(&cancellable),
892                    move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
893                        tx.send(t.propagate()).unwrap();
894                        l.quit();
895                    },
896                )
897            };
898            task.return_result(Err(glib::Error::new(
899                crate::IOErrorEnum::WouldBlock,
900                "WouldBlock",
901            )));
902        });
903
904        match fut {
905            Err(e) => match e.kind().unwrap() {
906                crate::IOErrorEnum::WouldBlock => {}
907                _ => panic!(),
908            },
909            Ok(_) => panic!(),
910        }
911    }
912
913    #[test]
914    fn test_cancelled() {
915        let fut = run_async_local(|tx, l| {
916            let cancellable = crate::Cancellable::new();
917            let task = unsafe {
918                crate::LocalTask::new(
919                    None,
920                    Some(&cancellable),
921                    move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
922                        tx.send(t.propagate()).unwrap();
923                        l.quit();
924                    },
925                )
926            };
927            cancellable.cancel();
928            task.return_error_if_cancelled();
929        });
930
931        match fut {
932            Err(e) => match e.kind().unwrap() {
933                crate::IOErrorEnum::Cancelled => {}
934                _ => panic!(),
935            },
936            Ok(_) => panic!(),
937        }
938    }
939
940    #[test]
941    fn test_spawn_blocking() {
942        let main_context = glib::MainContext::new();
943        main_context.block_on(async {
944            let x = super::spawn_blocking(|| 123).await;
945            assert_eq!(x.unwrap(), 123);
946        });
947    }
948}