Struct gio::Task

source ·
#[repr(transparent)]
pub struct Task<V: ValueType + Send> { /* private fields */ }
Expand description

Task provides idiomatic access to gio’s GTask API, for instance by being generic over their value type, while not completely departing from the underlying C API. Task is Send and Sync and requires its value to also be Send and Sync, thus is useful to to implement gio style asynchronous tasks that run in threads. If you need to only run tasks in glib main loop see the LocalTask type.

The constructors of LocalTask and Task is marked as unsafe because this API does not allow to automatically enforce all the invariants required to be a completely safe abstraction. The caller is responsible to ensure the following requirements are satisfied

  • You should not create a LocalTask, upcast it to a glib::Object and then downcast it to a Task, as this will bypass the thread safety requirements
  • You should ensure that the return_result, return_error_if_cancelled and propagate() methods are only called once. A Task represents and manages a cancellable “task”.

Asynchronous operations

The most common usage of Task is as a AsyncResult, to manage data during an asynchronous operation. You call new() in the “start” method, followed by [set_task_data()][Self::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 [return_pointer()][Self::return_pointer()] or [return_error()][Self::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] where it was created (waiting until the next iteration of the main loop first, if necessary). The caller will pass the Task back to the operation’s finish function (as a AsyncResult), and you can use [propagate_pointer()][Self::propagate_pointer()] or the like to extract the return value.

Using Task requires the thread-default glib::MainContext from when the Task was constructed to be running at least until the task has completed and its data has been freed.

Here is an example for using GTask as a GAsyncResult:

⚠️ The following code is in C ⚠️

    typedef struct {
      CakeFrostingType frosting;
      char *message;
    } DecorationData;

    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_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))
        {
          g_object_unref (cake);
          // g_task_return_error() takes ownership of error
          g_task_return_error (task, error);
          g_object_unref (task);
          return;
        }

      g_task_return_pointer (task, cake, g_object_unref);
      g_object_unref (task);
    }

    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;
        }

      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);
    }

Chained asynchronous operations

Task also tries to simplify asynchronous operations that internally chain together several smaller asynchronous operations. cancellable(), context(), and priority() allow you to get back the task’s Cancellable, glib::MainContext, and [I/O priority][io-priority] when starting a new subtask, so you don’t have to keep track of them yourself. [attach_source()][Self::attach_source()] simplifies the case of waiting for a source to fire (automatically using the correct glib::MainContext and priority).

Here is an example for chained asynchronous operations:

⚠️ The following code is in C ⚠️

    typedef struct {
      Cake *cake;
      CakeFrostingType frosting;
      char *message;
    } BakingData;

    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))
        {
          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);
    }

    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)
        {
          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
    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;

      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 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 Task was created.

Running a task in a thread:

⚠️ The following code is in 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
    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);
    }

    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 *
    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);
    }

Adding cancellability to uncancellable tasks

Finally, run_in_thread() and [run_in_thread_sync()][Self::run_in_thread_sync()] can be used to turn an uncancellable operation into a cancellable one. If you call set_return_on_cancel(), passing true, then if the task’s 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 synchronous variants of blocking APIs.

Cancelling a task:

⚠️ The following code is in 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)
        {
          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
    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);

      ...

      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 *
    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;

      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 GSimpleAsyncResult

Task’s API attempts to be simpler than GSimpleAsyncResult’s in several ways:

  • You can save task-specific data with [set_task_data()][Self::set_task_data()], and retrieve it later with [task_data()][Self::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, Task also keeps track of the [priority][io-priority], Cancellable, and 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.
  • return_error_if_cancelled() provides simplified handling for cancellation. In addition, cancellation overrides any other Task return value by default, like GSimpleAsyncResult does when g_simple_async_result_set_check_cancellable() is called. (You can use set_check_cancellable() to turn off that behavior.) On the other hand, run_in_thread() guarantees that it will always run your task_func, even if the task’s Cancellable is already cancelled before the task gets a chance to run; you can start your task_func with a return_error_if_cancelled() check if you need the old behavior.
  • The “return” methods (eg, [return_pointer()][Self::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. (Task automatically figures out whether the task’s callback can be invoked directly, or if it needs to be sent to another glib::MainContext, or delayed until the next iteration of the current glib::MainContext.)
  • The “finish” functions for Task based operations are generally much simpler than GSimpleAsyncResult ones, normally consisting of only a single call to [propagate_pointer()][Self::propagate_pointer()] or the like. Since [propagate_pointer()][Self::propagate_pointer()] “steals” the return value from the Task, 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 _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 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 AsyncResultExt::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 having come from the _async() wrapper function (for “short-circuit” results, such as when passing 0 to InputStreamExtManual::read_async()).

Implements

glib::ObjectExt, AsyncResultExt

Implementations§

Available on crate feature v2_60 only.
Available on crate feature v2_60 only.

Trait Implementations§

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more
Formats the value using the given formatter. Read more
Formats the value using the given formatter. Read more
Feeds this value into the given Hasher. Read more
Feeds a slice of this type into the given Hasher. Read more
This method returns an Ordering between self and other. Read more
Compares and returns the maximum of two values. Read more
Compares and returns the minimum of two values. Read more
Restrict a value to a certain interval. Read more
This method tests for self and other values to be equal, and is used by ==. Read more
This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason. Read more
This method returns an ordering between self and other values if one exists. Read more
This method tests less than (for self and other) and is used by the < operator. Read more
This method tests less than or equal to (for self and other) and is used by the <= operator. Read more
This method tests greater than (for self and other) and is used by the > operator. Read more
This method tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Returns the type identifier of Self.

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more
Upcasts an object to a superclass or interface T. Read more
Upcasts an object to a reference of its superclass or interface T. Read more
Tries to downcast to a subclass or interface implementor T. Read more
Tries to downcast to a reference of its subclass or interface implementor T. Read more
Tries to cast to an object of type T. This handles upcasting, downcasting and casting between interface and interface implementors. All checks are performed at runtime, while downcast and upcast will do many checks at compile-time already. Read more
Tries to cast to reference to an object of type T. This handles upcasting, downcasting and casting between interface and interface implementors. All checks are performed at runtime, while downcast and upcast will do many checks at compile-time already. Read more
Casts to T unconditionally. Read more
Casts to &T unconditionally. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Returns true if the object is an instance of (can be cast to) T.
Returns the type of the object.
Returns the ObjectClass of the object. Read more
Returns the class of the object.
Returns the class of the object in the given type T. Read more
Returns the interface T of the object. Read more
Sets the property property_name of the object to value value. Read more
Sets the property property_name of the object to value value. Read more
Sets multiple properties of the object at once. Read more
Sets multiple properties of the object at once. Read more
Gets the property property_name of the object and cast it to the type V. Read more
Gets the property property_name of the object. Read more
Check if the object has a property property_name of the given type_. Read more
Get the type of the property property_name of this object. Read more
Get the ParamSpec of the property property_name of this object.
Return all ParamSpec of the properties of this object.
Freeze all property notifications until the return guard object is dropped. Read more
Set arbitrary data on this object with the given key. Read more
Return previously set arbitrary data of this object with the given key. Read more
Retrieve previously set arbitrary data of this object with the given key. Read more
Set arbitrary data on this object with the given key. Read more
Return previously set arbitrary data of this object with the given key. Read more
Retrieve previously set arbitrary data of this object with the given key. Read more
Block a given signal handler. Read more
Unblock a given signal handler.
Stop emission of the currently emitted signal.
Stop emission of the currently emitted signal by the (possibly detailed) signal name.
Connect to the signal signal_name on this object. Read more
Connect to the signal signal_id on this object. Read more
Connect to the signal signal_name on this object. Read more
Connect to the signal signal_id on this object. Read more
Connect to the signal signal_name on this object. Read more
Connect to the signal signal_id on this object. Read more
Connect a closure to the signal signal_name on this object. Read more
Connect a closure to the signal signal_id on this object. Read more
Limits the lifetime of closure to the lifetime of the object. When the object’s reference count drops to zero, the closure will be invalidated. An invalidated closure will ignore any calls to invoke_with_values, or invoke when using Rust closures. Read more
Emit signal by signal id. Read more
Same as Self::emit but takes Value for the arguments.
Emit signal by its name. Read more
Emit signal by its name. Read more
Emit signal by its name with details. Read more
Emit signal by its name with details. Read more
Emit signal by signal id with details. Read more
Emit signal by signal id with details. Read more
Disconnect a previously connected signal handler.
Connect to the notify signal of the object. Read more
Connect to the notify signal of the object. Read more
Connect to the notify signal of the object. Read more
Notify that the given property has changed its value. Read more
Notify that the given property has changed its value. Read more
Downgrade this object to a weak reference.
Add a callback to be notified when the Object is disposed.
Add a callback to be notified when the Object is disposed. Read more
Bind property source_property on this object to the target_property on the target object. Read more
Returns the strong reference count of this object.
Runs the dispose mechanism of the object. Read more
Ensures that the type has been registered with the type system.
The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
Returns a SendValue clone of self.
Converts the given value to a String. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.