mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-23 20:46:14 +01:00
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:
parent
3d8f1dc203
commit
de56743bf6
199
gio/gtask.c
199
gio/gtask.c
@ -33,43 +33,39 @@
|
||||
#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 task’s 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 operation’s 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" -->
|
||||
* Here is an example for using `GTask` as a [iface@Gio.AsyncResult]:
|
||||
* ```c
|
||||
* typedef struct {
|
||||
* CakeFrostingType frosting;
|
||||
* char *message;
|
||||
@ -161,22 +157,23 @@
|
||||
*
|
||||
* 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 task’s
|
||||
* [class@Gio.Cancellable], [struct@GLib.MainContext], and
|
||||
* [I/O priority](iface.AsyncResult.html#io-priority)
|
||||
* when starting a new subtask, so you don’t 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" -->
|
||||
* ```c
|
||||
* typedef struct {
|
||||
* Cake *cake;
|
||||
* CakeFrostingType frosting;
|
||||
@ -259,7 +256,7 @@
|
||||
* GSource *source;
|
||||
*
|
||||
* source = cake_decorator_wait_source_new (cake);
|
||||
* // Attach @source to @task's GMainContext and have it call
|
||||
* // 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);
|
||||
@ -300,18 +297,18 @@
|
||||
*
|
||||
* 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" -->
|
||||
* ```c
|
||||
* typedef struct {
|
||||
* guint radius;
|
||||
* CakeFlavor flavor;
|
||||
@ -379,24 +376,24 @@
|
||||
*
|
||||
* 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 task’s
|
||||
* [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" -->
|
||||
* ```c
|
||||
* static void
|
||||
* bake_cake_thread (GTask *task,
|
||||
* gpointer source_object,
|
||||
@ -417,8 +414,8 @@
|
||||
* 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
|
||||
* // 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
|
||||
@ -426,11 +423,11 @@
|
||||
* 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
|
||||
* // 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.
|
||||
* // 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);
|
||||
* }
|
||||
@ -485,68 +482,69 @@
|
||||
* g_object_unref (task);
|
||||
* return cake;
|
||||
* }
|
||||
* ]|
|
||||
* ```
|
||||
*
|
||||
* ## Porting from GSimpleAsyncResult
|
||||
* ## 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 task’s [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 task’s 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 class’s 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 @source’s name to the task’s name (as set with
|
||||
* g_task_set_name()), if one has been set on the task and the source doesn’t
|
||||
|
Loading…
Reference in New Issue
Block a user