EBookClient: Use g_bus_watch_name_on_connection().
authorMatthew Barnes <mbarnes@redhat.com>
Sat, 9 Feb 2013 15:37:31 +0000 (10:37 -0500)
committerMatthew Barnes <mbarnes@redhat.com>
Sat, 9 Feb 2013 21:21:15 +0000 (16:21 -0500)
g_bus_watch_name_on_connection() handles "NameOwnerChanged" signals
as well as "closed" signals from the GDBusConnection.  In the event
the bus name vanishes, we schedule an idle callback on the client's
GMainContext to emit a "backend-died" signal.

Also while we're at it, call the close() method asynchronously from
dispose() so we don't block.

addressbook/libebook/e-book-client.c

index 79ff390..f13c623 100644 (file)
@@ -51,9 +51,9 @@ typedef struct _ConnectClosure ConnectClosure;
 typedef struct _RunInThreadClosure RunInThreadClosure;
 
 struct _EBookClientPrivate {
-       EDBusAddressBook *dbus_proxy;
        GMainContext *main_context;
-       guint gone_signal_id;
+       EDBusAddressBook *dbus_proxy;
+       guint name_watcher_id;
 
        gulong dbus_proxy_error_handler_id;
        gulong dbus_proxy_notify_handler_id;
@@ -285,87 +285,46 @@ set_proxy_gone_error (GError **error)
 }
 
 static volatile gint active_book_clients = 0;
-static guint book_connection_closed_id = 0;
+static guint book_factory_watcher_id = 0;
 static EDBusAddressBookFactory *book_factory = NULL;
 static GRecMutex book_factory_lock;
 #define LOCK_FACTORY()   g_rec_mutex_lock (&book_factory_lock)
 #define UNLOCK_FACTORY() g_rec_mutex_unlock (&book_factory_lock)
 
-static void gdbus_book_factory_closed_cb (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, gpointer user_data);
-
 static void
-gdbus_book_factory_disconnect (GDBusConnection *connection)
+book_factory_disconnect (void)
 {
        LOCK_FACTORY ();
 
-       if (!connection && book_factory)
-               connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (book_factory));
-
-       if (connection && book_connection_closed_id) {
-               g_dbus_connection_signal_unsubscribe (connection, book_connection_closed_id);
-               g_signal_handlers_disconnect_by_func (connection, gdbus_book_factory_closed_cb, NULL);
+       if (book_factory_watcher_id > 0) {
+               g_bus_unwatch_name (book_factory_watcher_id);
+               book_factory_watcher_id = 0;
        }
 
-       if (book_factory != NULL)
-               g_object_unref (book_factory);
-
-       book_connection_closed_id = 0;
-       book_factory = NULL;
+       g_clear_object (&book_factory);
 
        UNLOCK_FACTORY ();
 }
 
 static void
-gdbus_book_factory_closed_cb (GDBusConnection *connection,
-                              gboolean remote_peer_vanished,
-                              GError *error,
-                              gpointer user_data)
-{
-       GError *err = NULL;
-
-       LOCK_FACTORY ();
-
-       gdbus_book_factory_disconnect (connection);
-
-       if (error)
-               unwrap_dbus_error (g_error_copy (error), &err);
-
-       if (err) {
-               g_debug ("GDBus connection is closed%s: %s", remote_peer_vanished ? ", remote peer vanished" : "", err->message);
-               g_error_free (err);
-       } else if (active_book_clients > 0) {
-               g_debug ("GDBus connection is closed%s", remote_peer_vanished ? ", remote peer vanished" : "");
-       }
-
-       UNLOCK_FACTORY ();
-}
-
-static void
-gdbus_book_factory_connection_gone_cb (GDBusConnection *connection,
-                                       const gchar *sender_name,
-                                       const gchar *object_path,
-                                       const gchar *interface_name,
-                                       const gchar *signal_name,
-                                       GVariant *parameters,
-                                       gpointer user_data)
+book_factory_name_vanished_cb (GDBusConnection *connection,
+                               const gchar *name,
+                               gpointer user_data)
 {
-       /* signal subscription takes care of correct parameters,
-        * thus just do what is to be done here */
-       gdbus_book_factory_closed_cb (connection, TRUE, NULL, user_data);
+       book_factory_disconnect ();
 }
 
 static gboolean
-gdbus_book_factory_activate (GCancellable *cancellable,
-                             GError **error)
+book_factory_activate (GCancellable *cancellable,
+                       GError **error)
 {
-       GDBusConnection *connection;
+       GDBusProxy *proxy;
+       gboolean success = TRUE;
 
        LOCK_FACTORY ();
 
-       if (G_LIKELY (book_factory != NULL)) {
-               UNLOCK_FACTORY ();
-               return TRUE;
-       }
+       if (G_LIKELY (book_factory != NULL))
+               goto exit;
 
        book_factory = e_dbus_address_book_factory_proxy_new_for_bus_sync (
                G_BUS_TYPE_SESSION,
@@ -375,28 +334,24 @@ gdbus_book_factory_activate (GCancellable *cancellable,
                cancellable, error);
 
        if (book_factory == NULL) {
-               UNLOCK_FACTORY ();
-               return FALSE;
+               success = FALSE;
+               goto exit;
        }
 
-       connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (book_factory));
-       book_connection_closed_id = g_dbus_connection_signal_subscribe (
-               connection,
-               NULL,                                           /* sender */
-               "org.freedesktop.DBus",                         /* interface */
-               "NameOwnerChanged",                             /* member */
-               "/org/freedesktop/DBus",                        /* object_path */
-               "org.gnome.evolution.dataserver.AddressBook",   /* arg0 */
-               G_DBUS_SIGNAL_FLAGS_NONE,
-               gdbus_book_factory_connection_gone_cb, NULL, NULL);
-
-       g_signal_connect (
-               connection, "closed",
-               G_CALLBACK (gdbus_book_factory_closed_cb), NULL);
+       proxy = G_DBUS_PROXY (book_factory);
+
+       book_factory_watcher_id = g_bus_watch_name_on_connection (
+               g_dbus_proxy_get_connection (proxy),
+               g_dbus_proxy_get_name (proxy),
+               G_BUS_NAME_WATCHER_FLAGS_NONE,
+               (GBusNameAppearedCallback) NULL,
+               (GBusNameVanishedCallback) book_factory_name_vanished_cb,
+               NULL, (GDestroyNotify) NULL);
 
+exit:
        UNLOCK_FACTORY ();
 
-       return TRUE;
+       return success;
 }
 
 static gpointer
@@ -500,72 +455,16 @@ book_client_run_in_dbus_thread (GSimpleAsyncResult *simple,
        g_main_context_unref (main_context);
 }
 
-static void gdbus_book_client_disconnect (EBookClient *client);
-
-/*
- * Called when the addressbook server dies.
- */
-static void
-gdbus_book_client_closed_cb (GDBusConnection *connection,
-                             gboolean remote_peer_vanished,
-                             GError *error,
-                             EBookClient *client)
-{
-       GError *err = NULL;
-
-       g_assert (E_IS_BOOK_CLIENT (client));
-
-       if (error)
-               unwrap_dbus_error (g_error_copy (error), &err);
-
-       if (err) {
-               g_debug (G_STRLOC ": EBookClient GDBus connection is closed%s: %s", remote_peer_vanished ? ", remote peer vanished" : "", err->message);
-               g_error_free (err);
-       } else {
-               g_debug (G_STRLOC ": EBookClient GDBus connection is closed%s", remote_peer_vanished ? ", remote peer vanished" : "");
-       }
-
-       gdbus_book_client_disconnect (client);
-
-       e_client_emit_backend_died (E_CLIENT (client));
-}
-
-static void
-gdbus_book_client_connection_gone_cb (GDBusConnection *connection,
-                                      const gchar *sender_name,
-                                      const gchar *object_path,
-                                      const gchar *interface_name,
-                                      const gchar *signal_name,
-                                      GVariant *parameters,
-                                      gpointer user_data)
-{
-       /* signal subscription takes care of correct parameters,
-        * thus just do what is to be done here */
-       gdbus_book_client_closed_cb (connection, TRUE, NULL, user_data);
-}
-
-static void
-gdbus_book_client_disconnect (EBookClient *client)
+static gboolean
+book_client_emit_backend_died_idle_cb (gpointer user_data)
 {
-       g_return_if_fail (E_IS_BOOK_CLIENT (client));
-
-       /* Ensure that everything relevant is NULL */
-       LOCK_FACTORY ();
-
-       if (client->priv->dbus_proxy != NULL) {
-               GDBusConnection *connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (client->priv->dbus_proxy));
-
-               g_signal_handlers_disconnect_by_func (connection, gdbus_book_client_closed_cb, client);
-               g_dbus_connection_signal_unsubscribe (connection, client->priv->gone_signal_id);
-               client->priv->gone_signal_id = 0;
+       SignalClosure *signal_closure = user_data;
 
-               e_dbus_address_book_call_close_sync (
-                       client->priv->dbus_proxy, NULL, NULL);
-               g_object_unref (client->priv->dbus_proxy);
-               client->priv->dbus_proxy = NULL;
-       }
+       g_signal_emit_by_name (
+               signal_closure->client,
+               "backend-died");
 
-       UNLOCK_FACTORY ();
+       return FALSE;
 }
 
 static gboolean
@@ -703,6 +602,43 @@ book_client_dbus_proxy_notify_cb (EDBusAddressBook *dbus_proxy,
 }
 
 static void
+book_client_name_vanished_cb (GDBusConnection *connection,
+                              const gchar *name,
+                              EBookClient *book_client)
+{
+       GSource *idle_source;
+       SignalClosure *signal_closure;
+
+       signal_closure = g_slice_new0 (SignalClosure);
+       signal_closure->client = g_object_ref (book_client);
+
+       idle_source = g_idle_source_new ();
+       g_source_set_callback (
+               idle_source,
+               book_client_emit_backend_died_idle_cb,
+               signal_closure,
+               (GDestroyNotify) signal_closure_free);
+       g_source_attach (idle_source, book_client->priv->main_context);
+       g_source_unref (idle_source);
+}
+
+static void
+book_client_close_cb (GObject *source_object,
+                      GAsyncResult *result,
+                      gpointer user_data)
+{
+       GError *error = NULL;
+
+       e_dbus_address_book_call_close_finish (
+               E_DBUS_ADDRESS_BOOK (source_object), result, &error);
+
+       if (error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, error->message);
+               g_error_free (error);
+       }
+}
+
+static void
 book_client_dispose (GObject *object)
 {
        EBookClientPrivate *priv;
@@ -725,7 +661,15 @@ book_client_dispose (GObject *object)
                priv->dbus_proxy_notify_handler_id = 0;
        }
 
-       gdbus_book_client_disconnect (E_BOOK_CLIENT (object));
+       if (priv->dbus_proxy != NULL) {
+               /* Call close() asynchronously
+                * so we don't block dispose(). */
+               e_dbus_address_book_call_close (
+                       priv->dbus_proxy, NULL,
+                       book_client_close_cb, NULL);
+               g_object_unref (priv->dbus_proxy);
+               priv->dbus_proxy = NULL;
+       }
 
        if (priv->main_context != NULL) {
                g_main_context_unref (priv->main_context);
@@ -739,11 +683,18 @@ book_client_dispose (GObject *object)
 static void
 book_client_finalize (GObject *object)
 {
+       EBookClientPrivate *priv;
+
+       priv = E_BOOK_CLIENT_GET_PRIVATE (object);
+
+       if (priv->name_watcher_id > 0)
+               g_bus_unwatch_name (priv->name_watcher_id);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_book_client_parent_class)->finalize (object);
 
        if (g_atomic_int_dec_and_test (&active_book_clients))
-               gdbus_book_factory_disconnect (NULL);
+               book_factory_disconnect ();
 }
 
 static GDBusProxy *
@@ -916,7 +867,8 @@ book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
        EBookClientPrivate *priv;
        EClient *client;
        ESource *source;
-       GDBusConnection *connection;
+       GDBusProxy *factory_proxy;
+       GDBusProxy *proxy;
        const gchar *uid;
        gchar *object_path = NULL;
        gulong handler_id;
@@ -928,9 +880,7 @@ book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
        source = e_client_get_source (client);
        uid = e_source_get_uid (source);
 
-       LOCK_FACTORY ();
-       gdbus_book_factory_activate (cancellable, &error);
-       UNLOCK_FACTORY ();
+       book_factory_activate (cancellable, &error);
 
        if (error != NULL) {
                unwrap_dbus_error (error, &error);
@@ -946,20 +896,19 @@ book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
                ((object_path != NULL) && (error == NULL)) ||
                ((object_path == NULL) && (error != NULL)));
 
-       if (object_path == NULL) {
+       if (error != NULL) {
                unwrap_dbus_error (error, &error);
                g_simple_async_result_take_error (simple, error);
                return;
        }
 
-       connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (book_factory));
+       factory_proxy = G_DBUS_PROXY (book_factory);
 
        priv->dbus_proxy = e_dbus_address_book_proxy_new_sync (
-               connection,
+               g_dbus_proxy_get_connection (factory_proxy),
                G_DBUS_PROXY_FLAGS_NONE,
-               ADDRESS_BOOK_DBUS_SERVICE_NAME,
-               object_path,
-               cancellable, &error);
+               g_dbus_proxy_get_name (factory_proxy),
+               object_path, cancellable, &error);
 
        g_free (object_path);
 
@@ -974,39 +923,36 @@ book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
                return;
        }
 
-       g_dbus_proxy_set_default_timeout (
-               G_DBUS_PROXY (priv->dbus_proxy), DBUS_PROXY_TIMEOUT_MS);
+       /* Configure our new GDBusProxy. */
+
+       proxy = G_DBUS_PROXY (priv->dbus_proxy);
 
-       priv->gone_signal_id = g_dbus_connection_signal_subscribe (
-               connection,
-               "org.freedesktop.DBus",                         /* sender */
-               "org.freedesktop.DBus",                         /* interface */
-               "NameOwnerChanged",                             /* member */
-               "/org/freedesktop/DBus",                        /* object_path */
-               "org.gnome.evolution.dataserver.AddressBook",   /* arg0 */
-               G_DBUS_SIGNAL_FLAGS_NONE,
-               gdbus_book_client_connection_gone_cb, client, NULL);
+       g_dbus_proxy_set_default_timeout (proxy, DBUS_PROXY_TIMEOUT_MS);
 
-       g_signal_connect (
-               connection, "closed",
-               G_CALLBACK (gdbus_book_client_closed_cb), client);
+       priv->name_watcher_id = g_bus_watch_name_on_connection (
+               g_dbus_proxy_get_connection (proxy),
+               g_dbus_proxy_get_name (proxy),
+               G_BUS_NAME_WATCHER_FLAGS_NONE,
+               (GBusNameAppearedCallback) NULL,
+               (GBusNameVanishedCallback) book_client_name_vanished_cb,
+               client, (GDestroyNotify) NULL);
 
        handler_id = g_signal_connect_object (
-               priv->dbus_proxy, "error",
+               proxy, "error",
                G_CALLBACK (book_client_dbus_proxy_error_cb),
                client, 0);
        priv->dbus_proxy_error_handler_id = handler_id;
 
        handler_id = g_signal_connect_object (
-               priv->dbus_proxy, "notify",
+               proxy, "notify",
                G_CALLBACK (book_client_dbus_proxy_notify_cb),
                client, 0);
        priv->dbus_proxy_notify_handler_id = handler_id;
 
        /* Initialize our public-facing GObject properties. */
-       g_object_notify (G_OBJECT (priv->dbus_proxy), "online");
-       g_object_notify (G_OBJECT (priv->dbus_proxy), "writable");
-       g_object_notify (G_OBJECT (priv->dbus_proxy), "capabilities");
+       g_object_notify (G_OBJECT (proxy), "online");
+       g_object_notify (G_OBJECT (proxy), "writable");
+       g_object_notify (G_OBJECT (proxy), "capabilities");
 }
 
 static gboolean