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