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