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}