Integrating Libdex and GAsyncResult

Previously.

Historically if you wanted to do asynchronous work in GObject-based applications you would use GAsyncReadyCallback and GAsyncResult.

There are two ways to integrate with this form of asynchronous API.

In one direction, you can consume this historical API and provide the result as a DexFuture. In the other direction, you can provide this API in your application or library but implement it behind the scenes with DexFuture.

Converting GAsyncResult to Futures

A typical case to integrate, at least initially, is to extract the result of a GAsyncResult and propagate it to a future.

One way to do that is with a Dex.Promise which will resolve or reject from your async callback.

static void
my_callback (GObject      *object,
             GAsyncResult *result,
             gpointer      user_data)
{
  g_autoptr(DexPromise) promise = user_data;
  g_autoptr(GError) error = NULL;

  if (thing_finish (THING (object), result, &error))
    dex_promise_resolve_boolean (promise, TRUE);
  else
    dex_promise_reject (promise, g_steal_pointer (&error));
}

DexFuture *
my_wrapper (Thing *thing)
{
  DexPromise *promise = dex_promise_new_cancellable ();

  thing_async (thing,
               dex_promise_get_cancellable (promise),
               my_callback,
               dex_ref (promise));

  return DEX_FUTURE (promise);
}

Implementing AsyncResult with Futures

In some cases you may not want to “leak” into your API that you are using DexFuture. For example, you may want to only expose a traditional GIO API or maybe even clean up legacy code.

For these cases use Dex.AsyncResult. It is designed to feel familiar to those that have used GTask.

Dex.AsyncResult.new() allows taking the typical cancellable, callback, and user_data parameters similar to GTask.

Then call Dex.AsyncResult.await() providing the future that will resolve or reject with error. One completed, the users provided callback will be executed within the active scheduler at time of creation.

From your finish function, call the appropriate propgate API such as Dex.AsyncResult.propagate_int().

void
thing_async (Thing               *thing,
             GCancellable        *cancellable,
             GAsyncReadyCallback  callback,
             gpointer             user_data)
{
  g_autoptr(DexAsyncResult) result = NULL;

  result = dex_async_result_new (thing, cancellable, callback, user_data);
  dex_async_result_await (result, dex_future_new_true ());
}

gboolean
thing_finish (Thing         *thing,
              GAsyncResult  *result,
              GError       **error)
{
  return dex_async_result_propagate_boolean (DEX_ASYNC_RESULT (result), error);
}

Safety Notes

One thing that Libdex handles better than GTask is ensuring that your user_data is destroyed on the proper thread. The design of Dex.Block was done in such a way that both the result and user_data are passed back to the owning thread at the same time. This ensures that your user_data will never be finalized on the wrong thread.

This article can be found in the documentation at Integrating GAsyncResult.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.