How to call asynchronous function synchronously

GLib provides a lot of asynchronous functions, especially to deal with I/O. Unfortunately, some functions don’t have synchronous equivalents and the code has to be split into several callbacks. This is not handy in some cases. My this year’s GSoC student recently asked me whether it is possible to create synchronous function from asynchronous. He is currently working on test suite and don’t want to split test cases into several callbacks. So I decided to write a blog spot about as it might be handy for more people. Let’s explain it with an example on how to create g_file_mount_enclosing_volume_sync () from g_file_mount_enclosing_volume ():

typedef struct
{
  GMainLoop *loop;
  GAsyncResult *res;
} MountData;

static void
mount_cb (GObject *source_object,
          GAsyncResult *res,
          gpointer user_data)
{
  MountData *data = user_data;

  data->res = g_object_ref (res);
  g_main_loop_quit (data->loop);
}

static gboolean
g_file_mount_enclosing_volume_sync (GFile *location,
                                    GMountMountFlags flags,
                                    GMountOperation *mount_operation,
                                    GCancellable *cancellable,
                                    GError **error)
{
  g_autoptr (GMainContext) context  = NULL;
  g_autoptr (GMainLoop) loop = NULL;
  MountData data;
  gboolean retval;

  context = g_main_context_new ();
  loop = g_main_loop_new (context, FALSE);
 
  g_main_context_push_thread_default (context);

  data.loop = loop;
  g_file_mount_enclosing_volume (location,
                                 flags,
                                 mount_operation,
                                 cancellable,
                                 mount_cb,
                                 &data);
  g_main_loop_run (loop);
  retval = g_file_mount_enclosing_volume_finish (location, data.res, error);
  g_object_unref (data.res);

  g_main_context_pop_thread_default (context);

  return retval;
}

A similar approach can be seen on various places of GLib and GVfs source codes.

Join the Conversation

4 Comments

  1. You can actually simplify it a little by dropping the GMainLoop and using GMainContextPusher:

    static void
    mount_cb (GObject *source_object,
              GAsyncResult *res,
              gpointer user_data)
    {
      GAsyncResult **result_out = user_data;
    
      g_assert (*result_out == NULL);
      *result_out = g_object_ref (res);
    }
    
    static gboolean
    g_file_mount_enclosing_volume_sync (GFile *location,
                                        GMountMountFlags flags,
                                        GMountOperation *mount_operation,
                                        GCancellable *cancellable,
                                        GError **error)
    {
      g_autoptr(GMainContext) context  = NULL;
      g_autoptr(GMainContextPusher) pusher = NULL;
      g_autoptr(GAsyncResult) result = NULL;
    
      context = g_main_context_new ();
      pusher = g_main_context_pusher_new (context);
    
      g_file_mount_enclosing_volume (location,
                                     flags,
                                     mount_operation,
                                     cancellable,
                                     mount_cb,
                                     &result);
    
      while (result == NULL)
        g_main_context_iteration (context, TRUE);
    
      return g_file_mount_enclosing_volume_finish (location, result, error);
    }
    1. That’s a great improvement, thanks for the comment!

  2. Hi my usb is detected i can check it using sudo dmesg | grep -i usb but i am not able to see the the usb icon in Files also
    also my trash bin does not open every time i have run command for opening it would be great if you could help with same

Leave a comment

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