docs: Move the GTask SECTION

Move it to the struct docs.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>

Helps: #3037
This commit is contained in:
Philip Withnall 2023-11-14 15:12:21 +00:00
parent 3d8f1dc203
commit de56743bf6

View File

@ -33,520 +33,518 @@
#include <string.h>
/**
* SECTION:gtask
* @short_description: Cancellable synchronous or asynchronous task
* and result
* @include: gio/gio.h
* @see_also: #GAsyncResult
* GTask:
*
* A #GTask represents and manages a cancellable "task".
* A `GTask` represents and manages a cancellable task.
*
* ## Asynchronous operations
*
* The most common usage of #GTask is as a #GAsyncResult, to
* The most common usage of `GTask` is as a [iface@Gio.AsyncResult], to
* manage data during an asynchronous operation. You call
* g_task_new() in the "start" method, followed by
* g_task_set_task_data() and the like if you need to keep some
* [ctor@Gio.Task.new] in the start method, followed by
* [method@Gio.Task.set_task_data] and the like if you need to keep some
* additional data associated with the task, and then pass the
* task object around through your asynchronous operation.
* Eventually, you will call a method such as
* g_task_return_pointer() or g_task_return_error(), which will
* save the value you give it and then invoke the task's callback
* function in the
* [thread-default main context][g-main-context-push-thread-default]
* [method@Gio.Task.return_pointer] or [method@Gio.Task.return_error], which
* will save the value you give it and then invoke the tasks callback
* function in the thread-default main context (see
* [method@GLib.MainContext.push_thread_default])
* where it was created (waiting until the next iteration of the main
* loop first, if necessary). The caller will pass the #GTask back to
* the operation's finish function (as a #GAsyncResult), and you can
* use g_task_propagate_pointer() or the like to extract the
* loop first, if necessary). The caller will pass the `GTask` back to
* the operations finish function (as a [iface@Gio.AsyncResult]), and you can
* use [method@Gio.Task.propagate_pointer] or the like to extract the
* return value.
*
* Using #GTask requires the thread-default #GMainContext from when the
* #GTask was constructed to be running at least until the task has completed
* and its data has been freed.
* Using `GTask` requires the thread-default [struct@GLib.MainContext] from when
* the `GTask` was constructed to be running at least until the task has
* completed and its data has been freed.
*
* If a #GTask has been constructed and its callback set, it is an error to
* If a `GTask` has been constructed and its callback set, it is an error to
* not call `g_task_return_*()` on it. GLib will warn at runtime if this happens
* (since 2.76).
*
* Here is an example for using GTask as a GAsyncResult:
* |[<!-- language="C" -->
* typedef struct {
* CakeFrostingType frosting;
* char *message;
* } DecorationData;
* Here is an example for using `GTask` as a [iface@Gio.AsyncResult]:
* ```c
* typedef struct {
* CakeFrostingType frosting;
* char *message;
* } DecorationData;
*
* static void
* decoration_data_free (DecorationData *decoration)
* static void
* decoration_data_free (DecorationData *decoration)
* {
* g_free (decoration->message);
* g_slice_free (DecorationData, decoration);
* }
*
* static void
* baked_cb (Cake *cake,
* gpointer user_data)
* {
* GTask *task = user_data;
* DecorationData *decoration = g_task_get_task_data (task);
* GError *error = NULL;
*
* if (cake == NULL)
* {
* g_free (decoration->message);
* g_slice_free (DecorationData, decoration);
* g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR,
* "Go to the supermarket");
* g_object_unref (task);
* return;
* }
*
* static void
* baked_cb (Cake *cake,
* gpointer user_data)
* if (!cake_decorate (cake, decoration->frosting, decoration->message, &error))
* {
* GTask *task = user_data;
* DecorationData *decoration = g_task_get_task_data (task);
* GError *error = NULL;
* g_object_unref (cake);
* // g_task_return_error() takes ownership of error
* g_task_return_error (task, error);
* g_object_unref (task);
* return;
* }
*
* if (cake == NULL)
* {
* g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR,
* "Go to the supermarket");
* g_object_unref (task);
* return;
* }
* g_task_return_pointer (task, cake, g_object_unref);
* g_object_unref (task);
* }
*
* if (!cake_decorate (cake, decoration->frosting, decoration->message, &error))
* {
* g_object_unref (cake);
* // g_task_return_error() takes ownership of error
* g_task_return_error (task, error);
* g_object_unref (task);
* return;
* }
* void
* baker_bake_cake_async (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* {
* GTask *task;
* DecorationData *decoration;
* Cake *cake;
*
* task = g_task_new (self, cancellable, callback, user_data);
* if (radius < 3)
* {
* g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_TOO_SMALL,
* "%ucm radius cakes are silly",
* radius);
* g_object_unref (task);
* return;
* }
*
* cake = _baker_get_cached_cake (self, radius, flavor, frosting, message);
* if (cake != NULL)
* {
* // _baker_get_cached_cake() returns a reffed cake
* g_task_return_pointer (task, cake, g_object_unref);
* g_object_unref (task);
* return;
* }
*
* void
* baker_bake_cake_async (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* {
* GTask *task;
* DecorationData *decoration;
* Cake *cake;
* decoration = g_slice_new (DecorationData);
* decoration->frosting = frosting;
* decoration->message = g_strdup (message);
* g_task_set_task_data (task, decoration, (GDestroyNotify) decoration_data_free);
*
* task = g_task_new (self, cancellable, callback, user_data);
* if (radius < 3)
* {
* g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_TOO_SMALL,
* "%ucm radius cakes are silly",
* radius);
* g_object_unref (task);
* return;
* }
* _baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task);
* }
*
* cake = _baker_get_cached_cake (self, radius, flavor, frosting, message);
* if (cake != NULL)
* {
* // _baker_get_cached_cake() returns a reffed cake
* g_task_return_pointer (task, cake, g_object_unref);
* g_object_unref (task);
* return;
* }
* Cake *
* baker_bake_cake_finish (Baker *self,
* GAsyncResult *result,
* GError **error)
* {
* g_return_val_if_fail (g_task_is_valid (result, self), NULL);
*
* decoration = g_slice_new (DecorationData);
* decoration->frosting = frosting;
* decoration->message = g_strdup (message);
* g_task_set_task_data (task, decoration, (GDestroyNotify) decoration_data_free);
*
* _baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task);
* }
*
* Cake *
* baker_bake_cake_finish (Baker *self,
* GAsyncResult *result,
* GError **error)
* {
* g_return_val_if_fail (g_task_is_valid (result, self), NULL);
*
* return g_task_propagate_pointer (G_TASK (result), error);
* }
* ]|
* return g_task_propagate_pointer (G_TASK (result), error);
* }
* ```
*
* ## Chained asynchronous operations
*
* #GTask also tries to simplify asynchronous operations that
* `GTask` also tries to simplify asynchronous operations that
* internally chain together several smaller asynchronous
* operations. g_task_get_cancellable(), g_task_get_context(),
* and g_task_get_priority() allow you to get back the task's
* #GCancellable, #GMainContext, and [I/O priority][io-priority]
* when starting a new subtask, so you don't have to keep track
* of them yourself. g_task_attach_source() simplifies the case
* operations. [method@Gio.Task.get_cancellable], [method@Gio.Task.get_context],
* and [method@Gio.Task.get_priority] allow you to get back the tasks
* [class@Gio.Cancellable], [struct@GLib.MainContext], and
* [I/O priority](iface.AsyncResult.html#io-priority)
* when starting a new subtask, so you dont have to keep track
* of them yourself. [method@Gio.Task.attach_source] simplifies the case
* of waiting for a source to fire (automatically using the correct
* #GMainContext and priority).
* [struct@GLib.MainContext] and priority).
*
* Here is an example for chained asynchronous operations:
* |[<!-- language="C" -->
* typedef struct {
* Cake *cake;
* CakeFrostingType frosting;
* char *message;
* } BakingData;
* ```c
* typedef struct {
* Cake *cake;
* CakeFrostingType frosting;
* char *message;
* } BakingData;
*
* static void
* decoration_data_free (BakingData *bd)
* static void
* decoration_data_free (BakingData *bd)
* {
* if (bd->cake)
* g_object_unref (bd->cake);
* g_free (bd->message);
* g_slice_free (BakingData, bd);
* }
*
* static void
* decorated_cb (Cake *cake,
* GAsyncResult *result,
* gpointer user_data)
* {
* GTask *task = user_data;
* GError *error = NULL;
*
* if (!cake_decorate_finish (cake, result, &error))
* {
* if (bd->cake)
* g_object_unref (bd->cake);
* g_free (bd->message);
* g_slice_free (BakingData, bd);
* }
*
* static void
* decorated_cb (Cake *cake,
* GAsyncResult *result,
* gpointer user_data)
* {
* GTask *task = user_data;
* GError *error = NULL;
*
* if (!cake_decorate_finish (cake, result, &error))
* {
* g_object_unref (cake);
* g_task_return_error (task, error);
* g_object_unref (task);
* return;
* }
*
* // baking_data_free() will drop its ref on the cake, so we have to
* // take another here to give to the caller.
* g_task_return_pointer (task, g_object_ref (cake), g_object_unref);
* g_object_unref (cake);
* g_task_return_error (task, error);
* g_object_unref (task);
* return;
* }
*
* static gboolean
* decorator_ready (gpointer user_data)
* // baking_data_free() will drop its ref on the cake, so we have to
* // take another here to give to the caller.
* g_task_return_pointer (task, g_object_ref (cake), g_object_unref);
* g_object_unref (task);
* }
*
* static gboolean
* decorator_ready (gpointer user_data)
* {
* GTask *task = user_data;
* BakingData *bd = g_task_get_task_data (task);
*
* cake_decorate_async (bd->cake, bd->frosting, bd->message,
* g_task_get_cancellable (task),
* decorated_cb, task);
*
* return G_SOURCE_REMOVE;
* }
*
* static void
* baked_cb (Cake *cake,
* gpointer user_data)
* {
* GTask *task = user_data;
* BakingData *bd = g_task_get_task_data (task);
* GError *error = NULL;
*
* if (cake == NULL)
* {
* GTask *task = user_data;
* BakingData *bd = g_task_get_task_data (task);
*
* cake_decorate_async (bd->cake, bd->frosting, bd->message,
* g_task_get_cancellable (task),
* decorated_cb, task);
*
* return G_SOURCE_REMOVE;
* g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR,
* "Go to the supermarket");
* g_object_unref (task);
* return;
* }
*
* static void
* baked_cb (Cake *cake,
* gpointer user_data)
* bd->cake = cake;
*
* // Bail out now if the user has already cancelled
* if (g_task_return_error_if_cancelled (task))
* {
* GTask *task = user_data;
* BakingData *bd = g_task_get_task_data (task);
* GError *error = NULL;
*
* if (cake == NULL)
* {
* g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR,
* "Go to the supermarket");
* g_object_unref (task);
* return;
* }
*
* bd->cake = cake;
*
* // Bail out now if the user has already cancelled
* if (g_task_return_error_if_cancelled (task))
* {
* g_object_unref (task);
* return;
* }
*
* if (cake_decorator_available (cake))
* decorator_ready (task);
* else
* {
* GSource *source;
*
* source = cake_decorator_wait_source_new (cake);
* // Attach @source to @task's GMainContext and have it call
* // decorator_ready() when it is ready.
* g_task_attach_source (task, source, decorator_ready);
* g_source_unref (source);
* }
* g_object_unref (task);
* return;
* }
*
* void
* baker_bake_cake_async (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* gint priority,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* if (cake_decorator_available (cake))
* decorator_ready (task);
* else
* {
* GTask *task;
* BakingData *bd;
* GSource *source;
*
* task = g_task_new (self, cancellable, callback, user_data);
* g_task_set_priority (task, priority);
*
* bd = g_slice_new0 (BakingData);
* bd->frosting = frosting;
* bd->message = g_strdup (message);
* g_task_set_task_data (task, bd, (GDestroyNotify) baking_data_free);
*
* _baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task);
* source = cake_decorator_wait_source_new (cake);
* // Attach @source to @tasks GMainContext and have it call
* // decorator_ready() when it is ready.
* g_task_attach_source (task, source, decorator_ready);
* g_source_unref (source);
* }
* }
*
* Cake *
* baker_bake_cake_finish (Baker *self,
* GAsyncResult *result,
* GError **error)
* {
* g_return_val_if_fail (g_task_is_valid (result, self), NULL);
* void
* baker_bake_cake_async (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* gint priority,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* {
* GTask *task;
* BakingData *bd;
*
* return g_task_propagate_pointer (G_TASK (result), error);
* }
* ]|
* task = g_task_new (self, cancellable, callback, user_data);
* g_task_set_priority (task, priority);
*
* bd = g_slice_new0 (BakingData);
* bd->frosting = frosting;
* bd->message = g_strdup (message);
* g_task_set_task_data (task, bd, (GDestroyNotify) baking_data_free);
*
* _baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task);
* }
*
* Cake *
* baker_bake_cake_finish (Baker *self,
* GAsyncResult *result,
* GError **error)
* {
* g_return_val_if_fail (g_task_is_valid (result, self), NULL);
*
* return g_task_propagate_pointer (G_TASK (result), error);
* }
* ```
*
* ## Asynchronous operations from synchronous ones
*
* You can use g_task_run_in_thread() to turn a synchronous
* You can use [method@Gio.Task.run_in_thread] to turn a synchronous
* operation into an asynchronous one, by running it in a thread.
* When it completes, the result will be dispatched to the
* [thread-default main context][g-main-context-push-thread-default]
* where the #GTask was created.
* When it completes, the result will be dispatched to the thread-default main
* context (see [method@GLib.MainContext.push_thread_default]) where the `GTask`
* was created.
*
* Running a task in a thread:
* |[<!-- language="C" -->
* typedef struct {
* guint radius;
* CakeFlavor flavor;
* CakeFrostingType frosting;
* char *message;
* } CakeData;
* ```c
* typedef struct {
* guint radius;
* CakeFlavor flavor;
* CakeFrostingType frosting;
* char *message;
* } CakeData;
*
* static void
* cake_data_free (CakeData *cake_data)
* {
* g_free (cake_data->message);
* g_slice_free (CakeData, cake_data);
* }
* static void
* cake_data_free (CakeData *cake_data)
* {
* g_free (cake_data->message);
* g_slice_free (CakeData, cake_data);
* }
*
* static void
* bake_cake_thread (GTask *task,
* gpointer source_object,
* gpointer task_data,
* GCancellable *cancellable)
* {
* Baker *self = source_object;
* CakeData *cake_data = task_data;
* Cake *cake;
* GError *error = NULL;
* static void
* bake_cake_thread (GTask *task,
* gpointer source_object,
* gpointer task_data,
* GCancellable *cancellable)
* {
* Baker *self = source_object;
* CakeData *cake_data = task_data;
* Cake *cake;
* GError *error = NULL;
*
* cake = bake_cake (baker, cake_data->radius, cake_data->flavor,
* cake_data->frosting, cake_data->message,
* cancellable, &error);
* if (cake)
* g_task_return_pointer (task, cake, g_object_unref);
* else
* g_task_return_error (task, error);
* }
* cake = bake_cake (baker, cake_data->radius, cake_data->flavor,
* cake_data->frosting, cake_data->message,
* cancellable, &error);
* if (cake)
* g_task_return_pointer (task, cake, g_object_unref);
* else
* g_task_return_error (task, error);
* }
*
* void
* baker_bake_cake_async (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* {
* CakeData *cake_data;
* GTask *task;
* void
* baker_bake_cake_async (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* {
* CakeData *cake_data;
* GTask *task;
*
* cake_data = g_slice_new (CakeData);
* cake_data->radius = radius;
* cake_data->flavor = flavor;
* cake_data->frosting = frosting;
* cake_data->message = g_strdup (message);
* task = g_task_new (self, cancellable, callback, user_data);
* g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
* g_task_run_in_thread (task, bake_cake_thread);
* g_object_unref (task);
* }
* cake_data = g_slice_new (CakeData);
* cake_data->radius = radius;
* cake_data->flavor = flavor;
* cake_data->frosting = frosting;
* cake_data->message = g_strdup (message);
* task = g_task_new (self, cancellable, callback, user_data);
* g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
* g_task_run_in_thread (task, bake_cake_thread);
* g_object_unref (task);
* }
*
* Cake *
* baker_bake_cake_finish (Baker *self,
* GAsyncResult *result,
* GError **error)
* {
* g_return_val_if_fail (g_task_is_valid (result, self), NULL);
* Cake *
* baker_bake_cake_finish (Baker *self,
* GAsyncResult *result,
* GError **error)
* {
* g_return_val_if_fail (g_task_is_valid (result, self), NULL);
*
* return g_task_propagate_pointer (G_TASK (result), error);
* }
* ]|
* return g_task_propagate_pointer (G_TASK (result), error);
* }
* ```
*
* ## Adding cancellability to uncancellable tasks
*
* Finally, g_task_run_in_thread() and g_task_run_in_thread_sync()
* can be used to turn an uncancellable operation into a
* cancellable one. If you call g_task_set_return_on_cancel(),
* passing %TRUE, then if the task's #GCancellable is cancelled,
* it will return control back to the caller immediately, while
* allowing the task thread to continue running in the background
* (and simply discarding its result when it finally does finish).
* Finally, [method@Gio.Task.run_in_thread] and
* [method@Gio.Task.run_in_thread_sync] can be used to turn an uncancellable
* operation into a cancellable one. If you call
* [method@Gio.Task.set_return_on_cancel], passing `TRUE`, then if the tasks
* [class@Gio.Cancellable] is cancelled, it will return control back to the
* caller immediately, while allowing the task thread to continue running in the
* background (and simply discarding its result when it finally does finish).
* Provided that the task thread is careful about how it uses
* locks and other externally-visible resources, this allows you
* to make "GLib-friendly" asynchronous and cancellable
* to make GLib-friendly asynchronous and cancellable
* synchronous variants of blocking APIs.
*
* Cancelling a task:
* |[<!-- language="C" -->
* static void
* bake_cake_thread (GTask *task,
* gpointer source_object,
* gpointer task_data,
* GCancellable *cancellable)
* ```c
* static void
* bake_cake_thread (GTask *task,
* gpointer source_object,
* gpointer task_data,
* GCancellable *cancellable)
* {
* Baker *self = source_object;
* CakeData *cake_data = task_data;
* Cake *cake;
* GError *error = NULL;
*
* cake = bake_cake (baker, cake_data->radius, cake_data->flavor,
* cake_data->frosting, cake_data->message,
* &error);
* if (error)
* {
* Baker *self = source_object;
* CakeData *cake_data = task_data;
* Cake *cake;
* GError *error = NULL;
*
* cake = bake_cake (baker, cake_data->radius, cake_data->flavor,
* cake_data->frosting, cake_data->message,
* &error);
* if (error)
* {
* g_task_return_error (task, error);
* return;
* }
*
* // If the task has already been cancelled, then we don't want to add
* // the cake to the cake cache. Likewise, we don't want to have the
* // task get cancelled in the middle of updating the cache.
* // g_task_set_return_on_cancel() will return %TRUE here if it managed
* // to disable return-on-cancel, or %FALSE if the task was cancelled
* // before it could.
* if (g_task_set_return_on_cancel (task, FALSE))
* {
* // If the caller cancels at this point, their
* // GAsyncReadyCallback won't be invoked until we return,
* // so we don't have to worry that this code will run at
* // the same time as that code does. But if there were
* // other functions that might look at the cake cache,
* // then we'd probably need a GMutex here as well.
* baker_add_cake_to_cache (baker, cake);
* g_task_return_pointer (task, cake, g_object_unref);
* }
* g_task_return_error (task, error);
* return;
* }
*
* void
* baker_bake_cake_async (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* // If the task has already been cancelled, then we dont want to add
* // the cake to the cake cache. Likewise, we dont want to have the
* // task get cancelled in the middle of updating the cache.
* // g_task_set_return_on_cancel() will return %TRUE here if it managed
* // to disable return-on-cancel, or %FALSE if the task was cancelled
* // before it could.
* if (g_task_set_return_on_cancel (task, FALSE))
* {
* CakeData *cake_data;
* GTask *task;
*
* cake_data = g_slice_new (CakeData);
*
* ...
*
* task = g_task_new (self, cancellable, callback, user_data);
* g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
* g_task_set_return_on_cancel (task, TRUE);
* g_task_run_in_thread (task, bake_cake_thread);
* // If the caller cancels at this point, their
* // GAsyncReadyCallback wont be invoked until we return,
* // so we dont have to worry that this code will run at
* // the same time as that code does. But if there were
* // other functions that might look at the cake cache,
* // then wed probably need a GMutex here as well.
* baker_add_cake_to_cache (baker, cake);
* g_task_return_pointer (task, cake, g_object_unref);
* }
* }
*
* Cake *
* baker_bake_cake_sync (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* GCancellable *cancellable,
* GError **error)
* {
* CakeData *cake_data;
* GTask *task;
* Cake *cake;
* void
* baker_bake_cake_async (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* {
* CakeData *cake_data;
* GTask *task;
*
* cake_data = g_slice_new (CakeData);
* cake_data = g_slice_new (CakeData);
*
* ...
* ...
*
* task = g_task_new (self, cancellable, NULL, NULL);
* g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
* g_task_set_return_on_cancel (task, TRUE);
* g_task_run_in_thread_sync (task, bake_cake_thread);
* task = g_task_new (self, cancellable, callback, user_data);
* g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
* g_task_set_return_on_cancel (task, TRUE);
* g_task_run_in_thread (task, bake_cake_thread);
* }
*
* cake = g_task_propagate_pointer (task, error);
* g_object_unref (task);
* return cake;
* }
* ]|
* Cake *
* baker_bake_cake_sync (Baker *self,
* guint radius,
* CakeFlavor flavor,
* CakeFrostingType frosting,
* const char *message,
* GCancellable *cancellable,
* GError **error)
* {
* CakeData *cake_data;
* GTask *task;
* Cake *cake;
*
* ## Porting from GSimpleAsyncResult
* cake_data = g_slice_new (CakeData);
*
* ...
*
* task = g_task_new (self, cancellable, NULL, NULL);
* g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
* g_task_set_return_on_cancel (task, TRUE);
* g_task_run_in_thread_sync (task, bake_cake_thread);
*
* cake = g_task_propagate_pointer (task, error);
* g_object_unref (task);
* return cake;
* }
* ```
*
* ## Porting from [class@Gio.SimpleAsyncResult]
*
* #GTask's API attempts to be simpler than #GSimpleAsyncResult's
* `GTask`s API attempts to be simpler than [class@Gio.SimpleAsyncResult]s
* in several ways:
* - You can save task-specific data with g_task_set_task_data(), and
* retrieve it later with g_task_get_task_data(). This replaces the
* abuse of g_simple_async_result_set_op_res_gpointer() for the same
* purpose with #GSimpleAsyncResult.
* - In addition to the task data, #GTask also keeps track of the
* [priority][io-priority], #GCancellable, and
* #GMainContext associated with the task, so tasks that consist of
* a chain of simpler asynchronous operations will have easy access
*
* - You can save task-specific data with [method@Gio.Task.set_task_data], and
* retrieve it later with [method@Gio.Task.get_task_data]. This replaces the
* abuse of [method@Gio.SimpleAsyncResult.set_op_res_gpointer] for the same
* purpose with [class@Gio.SimpleAsyncResult].
* - In addition to the task data, `GTask` also keeps track of the
* [priority](iface.AsyncResult.html#io-priority), [class@Gio.Cancellable],
* and [struct@GLib.MainContext] associated with the task, so tasks that
* consist of a chain of simpler asynchronous operations will have easy access
* to those values when starting each sub-task.
* - g_task_return_error_if_cancelled() provides simplified
* - [method@Gio.Task.return_error_if_cancelled] provides simplified
* handling for cancellation. In addition, cancellation
* overrides any other #GTask return value by default, like
* #GSimpleAsyncResult does when
* g_simple_async_result_set_check_cancellable() is called.
* (You can use g_task_set_check_cancellable() to turn off that
* behavior.) On the other hand, g_task_run_in_thread()
* overrides any other `GTask` return value by default, like
* [class@Gio.SimpleAsyncResult] does when
* [method@Gio.SimpleAsyncResult.set_check_cancellable] is called.
* (You can use [method@Gio.Task.set_check_cancellable] to turn off that
* behavior.) On the other hand, [method@Gio.Task.run_in_thread]
* guarantees that it will always run your
* `task_func`, even if the task's #GCancellable
* `task_func`, even if the tasks [class@Gio.Cancellable]
* is already cancelled before the task gets a chance to run;
* you can start your `task_func` with a
* g_task_return_error_if_cancelled() check if you need the
* [method@Gio.Task.return_error_if_cancelled] check if you need the
* old behavior.
* - The "return" methods (eg, g_task_return_pointer())
* automatically cause the task to be "completed" as well, and
* there is no need to worry about the "complete" vs "complete
* in idle" distinction. (#GTask automatically figures out
* whether the task's callback can be invoked directly, or
* if it needs to be sent to another #GMainContext, or delayed
* until the next iteration of the current #GMainContext.)
* - The "finish" functions for #GTask based operations are generally
* much simpler than #GSimpleAsyncResult ones, normally consisting
* of only a single call to g_task_propagate_pointer() or the like.
* Since g_task_propagate_pointer() "steals" the return value from
* the #GTask, it is not necessary to juggle pointers around to
* - The return methods (eg, [method@Gio.Task.return_pointer])
* automatically cause the task to be completed as well, and
* there is no need to worry about the complete vs complete in idle
* distinction. (`GTask` automatically figures out
* whether the tasks callback can be invoked directly, or
* if it needs to be sent to another [struct@GLib.MainContext], or delayed
* until the next iteration of the current [struct@GLib.MainContext].)
* - The finish functions for `GTask` based operations are generally
* much simpler than [class@Gio.SimpleAsyncResult] ones, normally consisting
* of only a single call to [method@Gio.Task.propagate_pointer] or the like.
* Since [method@Gio.Task.propagate_pointer] steals the return value from
* the `GTask`, it is not necessary to juggle pointers around to
* prevent it from being freed twice.
* - With #GSimpleAsyncResult, it was common to call
* g_simple_async_result_propagate_error() from the
* - With [class@Gio.SimpleAsyncResult], it was common to call
* [method@Gio.SimpleAsyncResult.propagate_error] from the
* `_finish()` wrapper function, and have
* virtual method implementations only deal with successful
* returns. This behavior is deprecated, because it makes it
* difficult for a subclass to chain to a parent class's async
* difficult for a subclass to chain to a parent classs async
* methods. Instead, the wrapper function should just be a
* simple wrapper, and the virtual method should call an
* appropriate `g_task_propagate_` function.
* Note that wrapper methods can now use
* g_async_result_legacy_propagate_error() to do old-style
* #GSimpleAsyncResult error-returning behavior, and
* g_async_result_is_tagged() to check if a result is tagged as
* [method@Gio.AsyncResult.legacy_propagate_error] to do old-style
* [class@Gio.SimpleAsyncResult] error-returning behavior, and
* [method@Gio.AsyncResult.is_tagged] to check if a result is tagged as
* having come from the `_async()` wrapper
* function (for "short-circuit" results, such as when passing
* 0 to g_input_stream_read_async()).
* function (for short-circuit results, such as when passing
* `0` to [method@Gio.InputStream.read_async]).
*
* ## Thread-safety considerations
*
* Due to some infelicities in the API design, there is a
* thread-safety concern that users of GTask have to be aware of:
* thread-safety concern that users of `GTask` have to be aware of:
*
* If the `main` thread drops its last reference to the source object
* or the task data before the task is finalized, then the finalizers
@ -556,19 +554,11 @@
* can lead to hard-to-debug crashes. Possible workarounds include:
*
* - Clear task data in a signal handler for `notify::completed`
*
* - Keep iterating a main context in the main thread and defer
* dropping the reference to the source object to that main
* context when the task is finalized
*/
/**
* GTask:
*
* The opaque object representing a synchronous or asynchronous task
* and its result.
*/
struct _GTask {
GObject parent_instance;
@ -962,7 +952,7 @@ g_task_set_task_data (GTask *task,
/**
* g_task_set_priority:
* @task: the #GTask
* @priority: the [priority][io-priority] of the request
* @priority: the [priority](iface.AsyncResult.html#io-priority) of the request
*
* Sets @task's priority. If you do not call this, it will default to
* %G_PRIORITY_DEFAULT.
@ -1787,8 +1777,9 @@ g_task_run_in_thread_sync (GTask *task,
*
* A utility function for dealing with async operations where you need
* to wait for a #GSource to trigger. Attaches @source to @task's
* #GMainContext with @task's [priority][io-priority], and sets @source's
* callback to @callback, with @task as the callback's `user_data`.
* #GMainContext with @task's [priority](iface.AsyncResult.html#io-priority),
* and sets @source's callback to @callback, with @task as the callback's
* `user_data`.
*
* It will set the @sources name to the tasks name (as set with
* g_task_set_name()), if one has been set on the task and the source doesnt