Asynchronous DBus Calls

Posted by Ross Burton on May 12, 2005

This week I have been mostly playing with asynchronous calls in the DBus GLib bindings. DBus is inherently an asynchronous system, but the present GLib bindings wrap that with the GProxy object into a quite simple asynchronous interface, a simple synchronous interface, and a tool to generate incredibly simple bindings.

The synchronous interface is trivial: dbus_g_proxy_invoke. This will make a call with arguments, specified as addresses for arguments to send and locations to put return values in. It blocks until a reply is returned, and works well.

char **names;
dbus_g_proxy_invoke (proxy, "ListNames", error, G_TYPE_INVALID, G_TYPE_STRV, &names, G_TYPE_INVALID);

The asynchronous interface is pretty simple. First call dbus_g_proxy_begin_call() to send the message. This returns a DBusGPendingCall object, on which you call dbus_g_pending_call_set_notify to set a function which is called when a reply is received. Inside the callback, dbus_g_proxy_end_call will get the return arguments.

static void callback(DBusGPendingCall *call, DBusGProxy *proxy) {
  GError *error = NULL;
  char **name_list;
  int name_list_len, i;

  if (!dbus_g_proxy_end_call (proxy, call, &error,
                              DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
                              &name_list, &name_list_len,
                              DBUS_TYPE_INVALID)) {
      g_printerr ("Failed to complete ListNames call: %s\n", error->message);
      g_error_free (error);
      g_main_loop_quit (loop);
  }

  g_print ("Names on the message bus:\n");
  for (i = 0; i < name_list_len; ++i) {
      g_print ("  %s\n", name_list[i]);
  }
  g_strfreev (name_list);

  dbus_g_pending_call_unref (call);
  g_main_loop_quit (loop);
}

int main (int argc, char **argv) {
  DBusGConnection *connection;
  GError *error = NULL;
  DBusGProxy *proxy;
  DBusGPendingCall *call;

  g_type_init ();
  loop = g_main_loop_new (NULL, TRUE);

  connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
  proxy = dbus_g_proxy_new_for_name (connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);

  call = dbus_g_proxy_begin_call (proxy, "ListNames", DBUS_TYPE_INVALID);
  dbus_g_pending_call_set_notify(call, (DBusGPendingCallNotify)callback, proxy, g_object_unref);
  g_main_loop_run (loop);
  return 0;
}

Asynchronous method calls are possible in the GLib bindings, but are not wrapped by the bindings generator at the moment. As an example, this is the prototype for the generated binding of the ListNames call:

gboolean org_freedesktop_DBus_list_names (DBusGProxy *proxy, char *** OUT_names, GError **error);

Where OUT_names is a pointer to a string array. Now, wouldn't it be nice if the bindings could generate asynchronous wrappers too:

static void callback (char **names, GError *error, gpointer userdata) {
  char **i;
  g_print ("Names on the message bus:\n");
  for (i = names; *i; i++) {
    g_print ("  %s\n", *i);
  }
}
...
org_freedesktop_DBus_list_names_async (proxy, callback, NULL);

That was copied from working code by the way. Hopefully it will pass the Havoc test and get into CVS!

NP: Means of Production, Aim

tags: tech