Iterating a GMainContext without using a GMainLoop

tl;dr: Use g_main_context_iteration() in a loop with a termination condition; when changing that condition, call g_main_context_wakeup().

GMainLoop is a bit of a pain to use if you want to run a main context with non-trivial termination conditions, since you need to put g_main_loop_quit() calls in various places, and the logic for terminating the loop becomes quite spread out.

Instead, it’s better to iterate the underlying GMainContext directly, like this:

while (async_result == NULL)
  g_main_context_iteration (context, TRUE);

where your termination condition is async_result != NULL. When changing the termination condition, call g_main_context_wakeup() to ensure the current iteration of the main context is unblocked in order to check the condition again. (This is technically only necessary if you’re changing the condition from another thread, in which case you also want to get/set the condition atomically; but it’s a good habit to get into anyway.)

This allows an easy pattern for turning an asynchronous operation into a synchronous one, which can be quite useful in unit tests:

typedef struct
{
  GAsyncResult **result_out;
  GMainContext  *context;
} ContextData;

static void
async_result_cb (GObject      *obj,
                 GAsyncResult *result,
                 gpointer      user_data)
{
  ContextData *data = user_data;
  *data->result_out = g_object_ref (result);
  g_main_context_wakeup (data->context);
}

static void
test_function (void)
{
  …

  g_autoptr(GAsyncResult) result = NULL;
  g_autoptr(GMainContext) context = g_main_context_new ();
  ContextData data = { &result, context };
  some_operation_async (…, async_result_cb, &data);

  while (result == NULL)
    g_main_context_iteration (data.context, TRUE);

  g_autoptr(GError) error = NULL;
  gboolean retval = some_operation_finish (…, result, &error);

  …
}

Leave a Reply

Your email address will not be published. Required fields are marked *

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