[glib/gdbus: 2/6] Add GBusNameWatcher and fix singleton handling in GDBusConnection



commit 908ab223cfeb967ef3970231357371ae3ea4ec14
Author: David Zeuthen <davidz redhat com>
Date:   Tue Apr 21 03:34:29 2009 -0400

    Add GBusNameWatcher and fix singleton handling in GDBusConnection
---
 docs/reference/gdbus/gdbus-docs.xml     |    1 +
 docs/reference/gdbus/gdbus-sections.txt |   21 +
 docs/reference/gdbus/gdbus.types        |    1 +
 gdbus/Makefile.am                       |    2 +
 gdbus/gbusnameowner.c                   |   89 +--
 gdbus/gbusnamewatcher.c                 | 1231 +++++++++++++++++++++++++++++++
 gdbus/gbusnamewatcher.h                 |  113 +++
 gdbus/gdbus.h                           |    1 +
 gdbus/gdbus.symbols                     |   11 +
 gdbus/gdbusconnection.c                 |  112 +++-
 gdbus/gdbustypes.h                      |    1 +
 gdbus/tests/connection.c                |  323 ++++++++-
 12 files changed, 1827 insertions(+), 79 deletions(-)

diff --git a/docs/reference/gdbus/gdbus-docs.xml b/docs/reference/gdbus/gdbus-docs.xml
index 3747302..710ca4c 100644
--- a/docs/reference/gdbus/gdbus-docs.xml
+++ b/docs/reference/gdbus/gdbus-docs.xml
@@ -25,6 +25,7 @@
     <chapter id="convenience">
         <title>GDBus Convenience</title>
         <xi:include href="xml/gbusnameowner.xml"/>
+        <xi:include href="xml/gbusnamewatcher.xml"/>
     </chapter>
     <chapter id="cmapping">
         <title>C Object Mapping</title>
diff --git a/docs/reference/gdbus/gdbus-sections.txt b/docs/reference/gdbus/gdbus-sections.txt
index 2640590..0550195 100644
--- a/docs/reference/gdbus/gdbus-sections.txt
+++ b/docs/reference/gdbus/gdbus-sections.txt
@@ -23,6 +23,27 @@ G_BUS_NAME_OWNER_GET_CLASS
 </SECTION>
 
 <SECTION>
+<FILE>gbusnamewatcher</FILE>
+<TITLE>GBusNameWatcher</TITLE>
+GBusNameWatcher
+GBusNameWatcherClass
+g_bus_name_watcher_new
+g_bus_name_watcher_new_finish
+g_bus_name_watcher_get_name
+g_bus_name_watcher_get_name_owner
+g_bus_name_watcher_get_flags
+g_bus_name_watcher_get_connection
+<SUBSECTION Standard>
+G_BUS_NAME_WATCHER
+G_IS_BUS_NAME_WATCHER
+G_TYPE_BUS_NAME_WATCHER
+g_bus_name_watcher_get_type
+G_BUS_NAME_WATCHER_CLASS
+G_IS_BUS_NAME_WATCHER_CLASS
+G_BUS_NAME_WATCHER_GET_CLASS
+</SECTION>
+
+<SECTION>
 <FILE>gdbusconnection</FILE>
 <TITLE>GDBusConnection</TITLE>
 GMessageBusType
diff --git a/docs/reference/gdbus/gdbus.types b/docs/reference/gdbus/gdbus.types
index 778d932..c57eda6 100644
--- a/docs/reference/gdbus/gdbus.types
+++ b/docs/reference/gdbus/gdbus.types
@@ -1,5 +1,6 @@
 g_dbus_connection_get_type
 g_bus_name_owner_get_type
+g_bus_name_watcher_get_type
 g_message_bus_type_get_type
 g_bus_name_owner_flags_get_type
 g_dbus_error_get_type
diff --git a/gdbus/Makefile.am b/gdbus/Makefile.am
index 466e059..1d702ec 100644
--- a/gdbus/Makefile.am
+++ b/gdbus/Makefile.am
@@ -80,6 +80,7 @@ gdbus_headers =				\
 	gdbusmainloop.h			\
 	gdbusconnection.h		\
 	gbusnameowner.h			\
+	gbusnamewatcher.h		\
 	$(NULL)
 
 libgdbus_2_0_la_SOURCES =						\
@@ -92,6 +93,7 @@ libgdbus_2_0_la_SOURCES =						\
 	gdbusmainloop.h			gdbusmainloop.c			\
 	gdbusconnection.h		gdbusconnection.c		\
 	gbusnameowner.h			gbusnameowner.c			\
+	gbusnamewatcher.h		gbusnamewatcher.c		\
 	gdbusprivate.h			gdbusprivate.c			\
 	gdbusalias.h 							\
 	gdbusaliasdef.c							\
diff --git a/gdbus/gbusnameowner.c b/gdbus/gbusnameowner.c
index c90f2ca..9f6185a 100644
--- a/gdbus/gbusnameowner.c
+++ b/gdbus/gbusnameowner.c
@@ -223,33 +223,6 @@ g_bus_name_owner_set_property (GObject      *object,
     }
 }
 
-#if 0
-static void
-request_name_cb (GMessageBusConnection *bus,
-                 GAsyncResult          *res,
-                 gpointer               user_data)
-{
-  GMessageBusRequestNameFlags reply;
-  GBusNameOwner *owner = G_BUS_NAME_OWNER (user_data);
-
-  reply = g_message_bus_connection_request_name_finish (bus,
-                                                        res,
-                                                        NULL);
-  if (reply == G_MESSAGE_BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
-    {
-      owner->priv->owns_name = TRUE;
-      g_object_notify (G_OBJECT (owner), "owns-name");
-      g_signal_emit (owner, signals[NAME_ACQUIRED_SIGNAL], 0);
-    }
-  else
-    {
-      //g_signal_emit (owner, signals[FAILED_TO_ACQUIRE_NAME_SIGNAL], 0);
-    }
-
-  g_object_unref (owner);
-}
-#endif
-
 #define PRINT_MESSAGE(message)                          \
   do {                                                  \
     const gchar *message_type;                          \
@@ -556,9 +529,10 @@ g_bus_name_owner_class_init (GBusNameOwnerClass *klass)
 }
 
 static void
-g_bus_name_owner_init (GBusNameOwner *connection)
+g_bus_name_owner_init (GBusNameOwner *owner)
 {
-  connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, G_TYPE_BUS_NAME_OWNER, GBusNameOwnerPrivate);
+  owner->priv = G_TYPE_INSTANCE_GET_PRIVATE (owner, G_TYPE_BUS_NAME_OWNER, GBusNameOwnerPrivate);
+  owner->priv->in_construction_phase = TRUE;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -579,15 +553,17 @@ bus_release_name_cb (DBusPendingCall *pending,
   g_assert (reply != NULL);
 
   dbus_error_init (&dbus_error);
-  if (dbus_message_get_args (reply,
-                             &dbus_error,
-                             DBUS_TYPE_UINT32, &release_name_reply,
-                             DBUS_TYPE_INVALID))
+  if (!dbus_set_error_from_message (reply, &dbus_error))
     {
-      g_simple_async_result_set_op_res_gpointer (simple, GINT_TO_POINTER (release_name_reply), NULL);
-      goto done;
+      if (dbus_message_get_args (reply,
+                                 &dbus_error,
+                                 DBUS_TYPE_UINT32, &release_name_reply,
+                                 DBUS_TYPE_INVALID))
+        {
+          g_simple_async_result_set_op_res_gpointer (simple, GINT_TO_POINTER (release_name_reply), NULL);
+          goto done;
+        }
     }
-
   error = g_dbus_error_new_for_dbus_error (&dbus_error,
                                            NULL,
                                            NULL);
@@ -730,18 +706,21 @@ bus_release_name_sync (GDBusConnection       *connection,
       goto out;
     }
 
-  if (!dbus_message_get_args (reply,
-                              &dbus_error,
-                              DBUS_TYPE_UINT32, &release_name_reply,
-                              DBUS_TYPE_INVALID))
+  if (!dbus_set_error_from_message (&dbus_error, reply))
     {
-      g_dbus_error_set_dbus_error (error,
-                                   &dbus_error,
-                                   NULL,
-                                   NULL);
-      dbus_error_free (&dbus_error);
-      goto out;
+      if (dbus_message_get_args (reply,
+                                 &dbus_error,
+                                 DBUS_TYPE_UINT32, &release_name_reply,
+                                 DBUS_TYPE_INVALID))
+        {
+          goto out;
+        }
     }
+  g_dbus_error_set_dbus_error (error,
+                               &dbus_error,
+                               NULL,
+                               NULL);
+  dbus_error_free (&dbus_error);
 
  out:
   if (message != NULL)
@@ -782,15 +761,17 @@ bus_request_name_cb (DBusPendingCall *pending,
   g_assert (reply != NULL);
 
   dbus_error_init (&dbus_error);
-  if (dbus_message_get_args (reply,
-                             &dbus_error,
-                             DBUS_TYPE_UINT32, &request_name_reply,
-                             DBUS_TYPE_INVALID))
+  if (!dbus_set_error_from_message (&dbus_error, reply))
     {
-      g_simple_async_result_set_op_res_gpointer (simple, GINT_TO_POINTER (request_name_reply), NULL);
-      goto done;
+      if (dbus_message_get_args (reply,
+                                 &dbus_error,
+                                 DBUS_TYPE_UINT32, &request_name_reply,
+                                 DBUS_TYPE_INVALID))
+        {
+          g_simple_async_result_set_op_res_gpointer (simple, GINT_TO_POINTER (request_name_reply), NULL);
+          goto done;
+        }
     }
-
   error = g_dbus_error_new_for_dbus_error (&dbus_error,
                                            NULL,
                                            NULL);
@@ -1039,7 +1020,6 @@ g_bus_name_owner_new (GMessageBusType        bus_type,
                                           NULL));
 
   data->owner = owner;
-  owner->priv->in_construction_phase = TRUE;
 
   g_object_unref (connection);
 
@@ -1110,7 +1090,6 @@ g_bus_name_owner_new_for_connection (GDBusConnection       *connection,
       data->user_data = user_data;
       data->is_for_connection = TRUE;
       data->owner = owner;
-      owner->priv->in_construction_phase = TRUE;
 
       /* connection is open.. try to claim the name */
       bus_request_name (data->owner->priv->connection,
diff --git a/gdbus/gbusnamewatcher.c b/gdbus/gbusnamewatcher.c
new file mode 100644
index 0000000..2e9f778
--- /dev/null
+++ b/gdbus/gbusnamewatcher.c
@@ -0,0 +1,1231 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+
+#include "gbusnamewatcher.h"
+#include "gdbusenumtypes.h"
+#include "gdbusconnection.h"
+#include "gdbuserror.h"
+#include "gdbusprivate.h"
+
+#include "gdbusalias.h"
+
+/**
+ * SECTION:gbusnamewatcher
+ * @short_description: Watch names on the bus
+ * @include: gdbus/gdbus.h
+ *
+ * #GBusNameWatcher is a utility class that makes it easy to track
+ * whether a name exists on a message bus. See
+ * g_bus_name_watcher_new() for an example.
+ */
+
+struct _GBusNameWatcherPrivate
+{
+  gchar *name;
+
+  GDBusConnection *connection;
+
+  gchar *name_owner;
+
+  gchar *match_rule;
+
+  /* these variables are used to store async callbacks for users calling _new()  */
+  gboolean   in_init_phase;
+  GPtrArray *init_phase_pending_simple_async_results;
+};
+
+enum
+{
+  PROP_0,
+  PROP_NAME,
+  PROP_NAME_OWNER,
+  PROP_CONNECTION,
+};
+
+enum
+{
+  NAME_APPEARED_SIGNAL,
+  NAME_VANISHED_SIGNAL,
+  LAST_SIGNAL,
+};
+
+G_LOCK_DEFINE_STATIC (watcher_lock);
+
+static DBusHandlerResult
+filter_function (DBusConnection *dbus_1_connection,
+                 DBusMessage    *message,
+                 void           *user_data);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static GBusNameWatcher *singletons_get_watcher (GDBusConnection *connection, const gchar *name);
+static void singletons_add_watcher (GBusNameWatcher *watcher);
+static void singletons_remove_watcher (GBusNameWatcher *watcher);
+
+static void bus_get_name_owner (GDBusConnection       *connection,
+                                const gchar           *name,
+                                GAsyncReadyCallback    callback,
+                                gpointer               user_data);
+static gchar *bus_get_name_owner_finish (GDBusConnection  *bus,
+                                         GAsyncResult     *res,
+                                         GError          **error);
+
+static void bus_add_match (GDBusConnection       *connection,
+                           const gchar           *match_rule,
+                           GAsyncReadyCallback    callback,
+                           gpointer               user_data);
+#if 0
+static gboolean bus_add_match_finish (GDBusConnection  *bus,
+                                      GAsyncResult     *res,
+                                      GError          **error);
+#endif
+
+static void bus_remove_match (GDBusConnection       *connection,
+                              const gchar           *match_rule,
+                              GAsyncReadyCallback    callback,
+                              gpointer               user_data);
+#if 0
+static gboolean bus_remove_match_finish (GDBusConnection  *bus,
+                                         GAsyncResult     *res,
+                                         GError          **error);
+#endif
+
+static void on_connection_opened (GDBusConnection *connection,
+                                  gpointer         user_data);
+
+static void on_connection_closed (GDBusConnection *connection,
+                                  gpointer         user_data);
+
+G_DEFINE_TYPE (GBusNameWatcher, g_bus_name_watcher, G_TYPE_OBJECT);
+
+static void
+g_bus_name_watcher_dispose (GObject *object)
+{
+  GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+  G_LOCK (watcher_lock);
+  singletons_remove_watcher (watcher);
+  G_UNLOCK (watcher_lock);
+
+  if (G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->dispose != NULL)
+    G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->dispose (object);
+}
+
+static void
+g_bus_name_watcher_finalize (GObject *object)
+{
+  GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+  if (watcher->priv->connection != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (watcher->priv->connection, on_connection_opened, watcher);
+      g_signal_handlers_disconnect_by_func (watcher->priv->connection, on_connection_closed, watcher);
+
+      if (g_dbus_connection_get_is_open (watcher->priv->connection))
+        {
+          bus_remove_match (watcher->priv->connection,
+                            watcher->priv->match_rule,
+                            NULL,
+                            NULL);
+
+          dbus_connection_remove_filter (g_dbus_connection_get_dbus_1_connection (watcher->priv->connection),
+                                         filter_function,
+                                         watcher);
+        }
+
+      g_object_unref (watcher->priv->connection);
+    }
+  g_free (watcher->priv->name);
+  g_free (watcher->priv->name_owner);
+  g_free (watcher->priv->match_rule);
+
+  if (G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->finalize (object);
+}
+
+static void
+g_bus_name_watcher_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, g_bus_name_watcher_get_name (watcher));
+      break;
+
+    case PROP_NAME_OWNER:
+      g_value_set_string (value, g_bus_name_watcher_get_name_owner (watcher));
+      break;
+
+    case PROP_CONNECTION:
+      g_value_set_object (value, g_bus_name_watcher_get_connection (watcher));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_bus_name_watcher_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      watcher->priv->name = g_value_dup_string (value);
+      break;
+
+    case PROP_CONNECTION:
+      watcher->priv->connection = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+#define PRINT_MESSAGE(message)                          \
+  do {                                                  \
+    const gchar *message_type;                          \
+    switch (dbus_message_get_type (message))            \
+      {                                                 \
+      case DBUS_MESSAGE_TYPE_METHOD_CALL:               \
+        message_type = "method_call";                   \
+        break;                                          \
+      case DBUS_MESSAGE_TYPE_METHOD_RETURN:             \
+        message_type = "method_return";                 \
+        break;                                          \
+      case DBUS_MESSAGE_TYPE_ERROR:                     \
+        message_type = "error";                         \
+        break;                                          \
+      case DBUS_MESSAGE_TYPE_SIGNAL:                    \
+        message_type = "signal";                        \
+        break;                                          \
+      case DBUS_MESSAGE_TYPE_INVALID:                   \
+        message_type = "invalid";                       \
+        break;                                          \
+      default:                                          \
+        message_type = "unknown";                       \
+        break;                                          \
+      }                                                 \
+    g_print ("new message:\n"                           \
+             " type:         %s\n"                      \
+             " sender:       %s\n"                      \
+             " destination:  %s\n"                      \
+             " path:         %s\n"                      \
+             " interface:    %s\n"                      \
+             " member:       %s\n",                     \
+             message_type,                              \
+             dbus_message_get_sender (message),         \
+             dbus_message_get_destination (message),    \
+             dbus_message_get_path (message),           \
+             dbus_message_get_interface (message),      \
+             dbus_message_get_member (message));        \
+  } while (FALSE)
+
+static DBusHandlerResult
+filter_function (DBusConnection *dbus_1_connection,
+                 DBusMessage    *message,
+                 void           *user_data)
+{
+  GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (user_data);
+  DBusError dbus_error;
+  const gchar *name;
+  const gchar *old_owner;
+  const gchar *new_owner;
+
+  g_debug ("in bus-name-watcher's filter_function for dbus_1_connection %p", dbus_1_connection);
+  PRINT_MESSAGE (message);
+
+  /* we only care about NameOwnerChanged */
+  if (!(dbus_message_is_signal (message,
+                                DBUS_INTERFACE_DBUS,
+                                "NameOwnerChanged") &&
+        g_strcmp0 (dbus_message_get_sender (message), DBUS_SERVICE_DBUS) == 0 &&
+        g_strcmp0 (dbus_message_get_path (message), DBUS_PATH_DBUS) == 0))
+    goto out;
+
+  /* extract parameters */
+  dbus_error_init (&dbus_error);
+  if (!dbus_message_get_args (message,
+                              &dbus_error,
+                              DBUS_TYPE_STRING, &name,
+                              DBUS_TYPE_STRING, &old_owner,
+                              DBUS_TYPE_STRING, &new_owner,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error extracting parameters for NameOwnerChanged signal: %s: %s",
+                 dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  /* we only care about a specific name */
+  if (g_strcmp0 (name, watcher->priv->name) != 0)
+    goto out;
+
+  g_debug ("name='%s' old_owner='%s' new_owner='%s'",
+           name,
+           old_owner,
+           new_owner);
+
+  if ((old_owner != NULL && strlen (old_owner) > 0) && watcher->priv->name_owner != NULL)
+    {
+      g_free (watcher->priv->name_owner);
+      watcher->priv->name_owner = NULL;
+      g_object_notify (G_OBJECT (watcher), "name-owner");
+      g_signal_emit (watcher, signals[NAME_VANISHED_SIGNAL], 0);
+    }
+
+  if (new_owner != NULL && strlen (new_owner) > 0)
+    {
+      g_warn_if_fail (watcher->priv->name_owner == NULL);
+      g_free (watcher->priv->name_owner);
+      watcher->priv->name_owner = g_strdup (new_owner);
+      g_object_notify (G_OBJECT (watcher), "name-owner");
+      g_signal_emit (watcher, signals[NAME_APPEARED_SIGNAL], 0);
+    }
+
+ out:
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+on_connection_opened (GDBusConnection *connection,
+                      gpointer         user_data)
+{
+  GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (user_data);
+
+  /* set up a filter function for listening on (some) NameOwnerChanged messages on the DBusConnection */
+  if (!dbus_connection_add_filter (g_dbus_connection_get_dbus_1_connection (watcher->priv->connection),
+                                   filter_function,
+                                   watcher,
+                                   NULL))
+    _g_dbus_oom ();
+
+  /* set up match rule */
+  bus_add_match (watcher->priv->connection,
+                 watcher->priv->match_rule,
+                 NULL,
+                 NULL);
+
+  /* TODO: if this is for a bus reconnection, we actually need check bus
+   * name again since someone may acquire the name before our match
+   * rules are added
+   */
+}
+
+static void
+on_connection_closed (GDBusConnection *connection,
+                      gpointer         user_data)
+{
+  GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (user_data);
+
+  /* no need to remove filter; it is removed when the DBusConnection is destroyed */
+
+  /* bus went down.. so emit ::name-vanished */
+  if (watcher->priv->name_owner != NULL)
+    {
+      g_free (watcher->priv->name_owner);
+      watcher->priv->name_owner = NULL;
+      g_object_notify (G_OBJECT (watcher), "name-owner");
+      g_signal_emit (watcher, signals[NAME_VANISHED_SIGNAL], 0);
+    }
+}
+
+static void
+g_bus_name_watcher_constructed (GObject *object)
+{
+  GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+  watcher->priv->match_rule = g_strdup_printf ("type='signal',"
+                                               "sender='org.freedesktop.DBus',"
+                                               "member='NameOwnerChanged',"
+                                               "arg0='%s'",
+                                               watcher->priv->name);
+
+  g_signal_connect (watcher->priv->connection, "opened", G_CALLBACK (on_connection_opened), watcher);
+  g_signal_connect (watcher->priv->connection, "closed", G_CALLBACK (on_connection_closed), watcher);
+  if (g_dbus_connection_get_is_open (watcher->priv->connection))
+    on_connection_opened (watcher->priv->connection, watcher);
+
+  if (G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->constructed (object);
+}
+
+static void
+g_bus_name_watcher_class_init (GBusNameWatcherClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  g_type_class_add_private (klass, sizeof (GBusNameWatcherPrivate));
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->dispose      = g_bus_name_watcher_dispose;
+  gobject_class->finalize     = g_bus_name_watcher_finalize;
+  gobject_class->constructed  = g_bus_name_watcher_constructed;
+  gobject_class->get_property = g_bus_name_watcher_get_property;
+  gobject_class->set_property = g_bus_name_watcher_set_property;
+
+  /**
+   * GBusNameWatcher:name:
+   *
+   * The name (well-known or unique) to watch.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_NAME,
+                                   g_param_spec_string ("name",
+                                                        _("name"),
+                                                        _("The name to watch"),
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+
+  /**
+   * GBusNameWatcher:name-owner:
+   *
+   * The unique name of the owner of the name being watched or %NULL
+   * if no-one is owning the name.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_NAME_OWNER,
+                                   g_param_spec_string ("name-owner",
+                                                        _("name-owner"),
+                                                        _("Owner of the name being watched"),
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+
+  /**
+   * GBusNameWatcher:connection:
+   *
+   * The #GMessageBusConnection used for watching the name.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_CONNECTION,
+                                   g_param_spec_object ("connection",
+                                                        _("connection"),
+                                                        _("The connection used for watching the name"),
+                                                        G_TYPE_DBUS_CONNECTION,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GBusNameWatcher::name-vanished:
+   * @watcher: The #GBusNameWatcher emitting the signal.
+   *
+   * Emitted when the name being watched vanishes or the connection is closed.
+   **/
+  signals[NAME_VANISHED_SIGNAL] = g_signal_new ("name-vanished",
+                                                G_TYPE_BUS_NAME_WATCHER,
+                                                G_SIGNAL_RUN_LAST,
+                                                G_STRUCT_OFFSET (GBusNameWatcherClass, name_vanished),
+                                                NULL,
+                                                NULL,
+                                                g_cclosure_marshal_VOID__VOID,
+                                                G_TYPE_NONE,
+                                                0);
+
+  /**
+   * GBusNameWatcher::name-appeared:
+   * @watcher: The #GBusNameWatcher emitting the signal.
+   *
+   * Emitted when the name being watched appears or the connection is opened.
+   **/
+  signals[NAME_APPEARED_SIGNAL] = g_signal_new ("name-appeared",
+                                                G_TYPE_BUS_NAME_WATCHER,
+                                                G_SIGNAL_RUN_LAST,
+                                                G_STRUCT_OFFSET (GBusNameWatcherClass, name_appeared),
+                                                NULL,
+                                                NULL,
+                                                g_cclosure_marshal_VOID__VOID,
+                                                G_TYPE_NONE,
+                                                0);
+}
+
+static void
+g_bus_name_watcher_init (GBusNameWatcher *watcher)
+{
+  watcher->priv = G_TYPE_INSTANCE_GET_PRIVATE (watcher, G_TYPE_BUS_NAME_WATCHER, GBusNameWatcherPrivate);
+  watcher->priv->in_init_phase = TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bus_add_match_cb (DBusPendingCall *pending,
+                  void            *user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  DBusMessage *reply;
+  DBusError dbus_error;
+  GError *error;
+
+  reply = dbus_pending_call_steal_reply (pending);
+  g_assert (reply != NULL);
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_set_error_from_message (&dbus_error, reply))
+    goto done;
+
+  error = g_dbus_error_new_for_dbus_error (&dbus_error,
+                                           NULL,
+                                           NULL);
+  g_simple_async_result_set_from_error (simple, error);
+  g_error_free (error);
+  dbus_error_free (&dbus_error);
+
+ done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+  dbus_message_unref (reply);
+}
+
+static void
+bus_add_match (GDBusConnection       *connection,
+               const gchar           *match_rule,
+               GAsyncReadyCallback    callback,
+               gpointer               user_data)
+{
+  GSimpleAsyncResult *simple;
+  DBusMessage *message;
+  DBusPendingCall *pending_call;
+
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail (match_rule != NULL);
+
+  simple = g_simple_async_result_new (G_OBJECT (connection),
+                                      callback,
+                                      user_data,
+                                      bus_add_match);
+
+  if (g_dbus_connection_get_dbus_1_connection (connection) == NULL)
+    {
+      g_simple_async_result_set_error (simple,
+                                       G_DBUS_ERROR,
+                                       G_DBUS_ERROR_FAILED,
+                                       _("Not connected to message bus"));
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+      goto out;
+    }
+
+  if ((message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                               DBUS_PATH_DBUS,
+                                               DBUS_INTERFACE_DBUS,
+                                               "AddMatch")) == NULL)
+    _g_dbus_oom ();
+  if (!dbus_message_append_args (message,
+                                 DBUS_TYPE_STRING, &match_rule,
+                                 DBUS_TYPE_INVALID))
+    _g_dbus_oom ();
+
+  if (!dbus_connection_send_with_reply (g_dbus_connection_get_dbus_1_connection (connection),
+                                        message,
+                                        &pending_call,
+                                        -1))
+    _g_dbus_oom ();
+
+  dbus_message_unref (message);
+
+  dbus_pending_call_set_notify (pending_call,
+                                bus_add_match_cb,
+                                simple,
+                                NULL);
+
+ out:
+  ;
+}
+
+#if 0
+static gboolean
+bus_add_match_finish (GDBusConnection  *bus,
+                      GAsyncResult     *res,
+                      GError          **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == bus_add_match);
+  return !g_simple_async_result_propagate_error (simple, error);
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bus_remove_match_cb (DBusPendingCall *pending,
+                     void            *user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  DBusMessage *reply;
+  DBusError dbus_error;
+  GError *error;
+
+  reply = dbus_pending_call_steal_reply (pending);
+  g_assert (reply != NULL);
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_set_error_from_message (&dbus_error, reply))
+    goto done;
+
+  error = g_dbus_error_new_for_dbus_error (&dbus_error,
+                                           NULL,
+                                           NULL);
+  g_simple_async_result_set_from_error (simple, error);
+  g_error_free (error);
+  dbus_error_free (&dbus_error);
+
+ done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+  dbus_message_unref (reply);
+}
+
+static void
+bus_remove_match (GDBusConnection       *connection,
+                  const gchar           *match_rule,
+                  GAsyncReadyCallback    callback,
+                  gpointer               user_data)
+{
+  GSimpleAsyncResult *simple;
+  DBusMessage *message;
+  DBusPendingCall *pending_call;
+
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail (match_rule != NULL);
+
+  simple = g_simple_async_result_new (G_OBJECT (connection),
+                                      callback,
+                                      user_data,
+                                      bus_remove_match);
+
+  if (g_dbus_connection_get_dbus_1_connection (connection) == NULL)
+    {
+      g_simple_async_result_set_error (simple,
+                                       G_DBUS_ERROR,
+                                       G_DBUS_ERROR_FAILED,
+                                       _("Not connected to message bus"));
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+      goto out;
+    }
+
+  if ((message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                               DBUS_PATH_DBUS,
+                                               DBUS_INTERFACE_DBUS,
+                                               "RemoveMatch")) == NULL)
+    _g_dbus_oom ();
+  if (!dbus_message_append_args (message,
+                                 DBUS_TYPE_STRING, &match_rule,
+                                 DBUS_TYPE_INVALID))
+    _g_dbus_oom ();
+
+  if (!dbus_connection_send_with_reply (g_dbus_connection_get_dbus_1_connection (connection),
+                                        message,
+                                        &pending_call,
+                                        -1))
+    _g_dbus_oom ();
+
+  dbus_message_unref (message);
+
+  dbus_pending_call_set_notify (pending_call,
+                                bus_remove_match_cb,
+                                simple,
+                                NULL);
+
+ out:
+  ;
+}
+
+#if 0
+static gboolean
+bus_remove_match_finish (GDBusConnection  *bus,
+                         GAsyncResult     *res,
+                         GError          **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == bus_remove_match);
+  return !g_simple_async_result_propagate_error (simple, error);
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bus_get_name_owner_cb (DBusPendingCall *pending,
+                       void            *user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  DBusMessage *reply;
+  DBusError dbus_error;
+  GError *error;
+  gchar *get_name_owner_reply;
+
+  reply = dbus_pending_call_steal_reply (pending);
+  g_assert (reply != NULL);
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_set_error_from_message (&dbus_error, reply))
+    {
+      if (dbus_message_get_args (reply,
+                                 &dbus_error,
+                                 DBUS_TYPE_STRING, &get_name_owner_reply,
+                                 DBUS_TYPE_INVALID))
+        {
+          g_simple_async_result_set_op_res_gpointer (simple, g_strdup (get_name_owner_reply), g_free);
+          goto done;
+        }
+    }
+  error = g_dbus_error_new_for_dbus_error (&dbus_error,
+                                           NULL,
+                                           NULL);
+  g_simple_async_result_set_from_error (simple, error);
+  g_error_free (error);
+  dbus_error_free (&dbus_error);
+
+ done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+  dbus_message_unref (reply);
+}
+
+static void
+bus_get_name_owner (GDBusConnection       *connection,
+                    const gchar           *name,
+                    GAsyncReadyCallback    callback,
+                    gpointer               user_data)
+{
+  GSimpleAsyncResult *simple;
+  DBusMessage *message;
+  DBusPendingCall *pending_call;
+
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail (name != NULL);
+
+  simple = g_simple_async_result_new (G_OBJECT (connection),
+                                      callback,
+                                      user_data,
+                                      bus_get_name_owner);
+
+  if (g_dbus_connection_get_dbus_1_connection (connection) == NULL)
+    {
+      g_simple_async_result_set_error (simple,
+                                       G_DBUS_ERROR,
+                                       G_DBUS_ERROR_FAILED,
+                                       _("Not connected to message bus"));
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+      goto out;
+    }
+
+  if ((message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                               DBUS_PATH_DBUS,
+                                               DBUS_INTERFACE_DBUS,
+                                               "GetNameOwner")) == NULL)
+    _g_dbus_oom ();
+  if (!dbus_message_append_args (message,
+                                 DBUS_TYPE_STRING, &name,
+                                 DBUS_TYPE_INVALID))
+    _g_dbus_oom ();
+
+  if (!dbus_connection_send_with_reply (g_dbus_connection_get_dbus_1_connection (connection),
+                                        message,
+                                        &pending_call,
+                                        -1))
+    _g_dbus_oom ();
+
+  dbus_message_unref (message);
+
+  dbus_pending_call_set_notify (pending_call,
+                                bus_get_name_owner_cb,
+                                simple,
+                                NULL);
+
+ out:
+  ;
+}
+
+static gchar *
+bus_get_name_owner_finish (GDBusConnection  *bus,
+                           GAsyncResult     *res,
+                           GError          **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gchar *ret;
+
+  ret = NULL;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == bus_get_name_owner);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    goto out;
+
+  ret = g_strdup (g_simple_async_result_get_op_res_gpointer (simple));
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* routines used for singleton handling */
+
+/* maps from connection to GHashTable (that maps from names to GBusNameWatcher) */
+static GHashTable *map_connection_to_map_of_watched_names_to_watcher = NULL;
+
+/* the following singletons_* functions must be called with watcher_lock held */
+
+/* called from _new() before creating a GBusNameWatcher - result is not reffed */
+static GBusNameWatcher *
+singletons_get_watcher (GDBusConnection *connection, const gchar *name)
+{
+  GBusNameWatcher *ret;
+  GHashTable *map_of_watched_names_to_watcher;
+
+  g_return_val_if_fail (connection != NULL, NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+
+  ret = NULL;
+
+  if (map_connection_to_map_of_watched_names_to_watcher == NULL)
+    goto out;
+
+  map_of_watched_names_to_watcher = g_hash_table_lookup (map_connection_to_map_of_watched_names_to_watcher,
+                                                         connection);
+  if (map_of_watched_names_to_watcher == NULL)
+    goto out;
+
+  ret = g_hash_table_lookup (map_of_watched_names_to_watcher,
+                             name);
+
+ out:
+  return ret;
+}
+
+/* called from _new() after creating the GBusNameWatcher */
+static void
+singletons_add_watcher (GBusNameWatcher *watcher)
+{
+  GHashTable *map_of_watched_names_to_watcher;
+
+  g_return_if_fail (watcher != NULL);
+
+  if (map_connection_to_map_of_watched_names_to_watcher == NULL)
+    map_connection_to_map_of_watched_names_to_watcher = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+  map_of_watched_names_to_watcher = g_hash_table_lookup (map_connection_to_map_of_watched_names_to_watcher,
+                                                         watcher->priv->connection);
+  if (map_of_watched_names_to_watcher == NULL)
+    {
+      map_of_watched_names_to_watcher = g_hash_table_new (g_str_hash, g_str_equal);
+      g_hash_table_insert (map_connection_to_map_of_watched_names_to_watcher,
+                           watcher->priv->connection,
+                           map_of_watched_names_to_watcher);
+    }
+
+  g_hash_table_insert (map_of_watched_names_to_watcher,
+                       watcher->priv->name,
+                       watcher);
+}
+
+/* called from dispose() - ie. can be called multiple times so don't assert anything */
+static void
+singletons_remove_watcher (GBusNameWatcher *watcher)
+{
+  GHashTable *map_of_watched_names_to_watcher;
+
+  if (map_connection_to_map_of_watched_names_to_watcher == NULL)
+    goto out;
+
+  map_of_watched_names_to_watcher = g_hash_table_lookup (map_connection_to_map_of_watched_names_to_watcher,
+                                                         watcher->priv->connection);
+  if (map_of_watched_names_to_watcher == NULL)
+    goto out;
+
+  if (!g_hash_table_remove (map_of_watched_names_to_watcher,
+                            watcher->priv->name))
+    goto out;
+
+  /* clean up as needed */
+  if (g_hash_table_size (map_of_watched_names_to_watcher) == 0)
+    {
+      g_hash_table_remove (map_connection_to_map_of_watched_names_to_watcher, watcher->priv->connection);
+      g_hash_table_destroy (map_of_watched_names_to_watcher);
+
+      if (g_hash_table_size (map_connection_to_map_of_watched_names_to_watcher) == 0)
+        {
+          g_hash_table_destroy (map_connection_to_map_of_watched_names_to_watcher);
+          map_connection_to_map_of_watched_names_to_watcher = NULL;
+        }
+    }
+
+ out:
+  ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GBusNameWatcher *watcher;
+} ConstructData;
+
+static void
+construct_data_free (ConstructData *data)
+{
+  g_free (data);
+}
+
+static void
+get_name_owner_cb (GDBusConnection *connection,
+                   GAsyncResult    *result,
+                   gpointer         user_data)
+{
+  ConstructData *data = user_data;
+  GSimpleAsyncResult *simple;
+  GError *error;
+  gchar *name_owner;
+  guint n;
+
+  error = NULL;
+  name_owner = bus_get_name_owner_finish (connection,
+                                          result,
+                                          &error);
+
+  G_LOCK (watcher_lock);
+
+  /* NAME_HAS_NO_OWNER error => don't report this as an error, it just means there is no name owner */
+  if (error != NULL && (error->domain != G_DBUS_ERROR && error->code != G_DBUS_ERROR_NAME_HAS_NO_OWNER))
+    {
+      /* report error back */
+      for (n = 0; n < data->watcher->priv->init_phase_pending_simple_async_results->len; n++)
+        {
+          simple = G_SIMPLE_ASYNC_RESULT (data->watcher->priv->init_phase_pending_simple_async_results->pdata[n]);
+          g_simple_async_result_set_from_error (simple, error);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+      g_error_free (error);
+    }
+  else
+    {
+      g_free (data->watcher->priv->name_owner);
+      if (error != NULL)
+        {
+          g_error_free (error);
+          data->watcher->priv->name_owner = NULL;
+        }
+      else
+        {
+          data->watcher->priv->name_owner = name_owner; /* steals the returned string */
+        }
+
+      /* complete operations without error */
+      for (n = 0; n < data->watcher->priv->init_phase_pending_simple_async_results->len; n++)
+        {
+          simple = G_SIMPLE_ASYNC_RESULT (data->watcher->priv->init_phase_pending_simple_async_results->pdata[n]);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+    }
+
+  data->watcher->priv->in_init_phase = FALSE;
+  g_ptr_array_free (data->watcher->priv->init_phase_pending_simple_async_results, TRUE);
+  G_UNLOCK (watcher_lock);
+
+
+  construct_data_free (data);
+}
+
+static void
+get_connection_cb (GDBusConnection *connection,
+                   GAsyncResult    *result,
+                   gpointer         user_data)
+{
+  ConstructData *data = user_data;
+  GError *error;
+
+  if (data->watcher == NULL)
+    goto out;
+
+  error = NULL;
+  if (!g_dbus_connection_bus_get_finish (connection,
+                                         result,
+                                         &error))
+    {
+      GSimpleAsyncResult *simple;
+      guint n;
+
+      G_LOCK (watcher_lock);
+      /* connection is not open.. report back */
+      for (n = 0; n < data->watcher->priv->init_phase_pending_simple_async_results->len; n++)
+        {
+          simple = G_SIMPLE_ASYNC_RESULT (data->watcher->priv->init_phase_pending_simple_async_results->pdata[n]);
+          g_simple_async_result_set_from_error (simple, error);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+      data->watcher->priv->in_init_phase = FALSE;
+      g_ptr_array_free (data->watcher->priv->init_phase_pending_simple_async_results, TRUE);
+      G_UNLOCK (watcher_lock);
+
+      g_error_free (error);
+
+      construct_data_free (data);
+    }
+  else
+    {
+      /* connection is open.. check if the name exists */
+      bus_get_name_owner (data->watcher->priv->connection,
+                          data->watcher->priv->name,
+                          (GAsyncReadyCallback) get_name_owner_cb,
+                          data);
+    }
+
+ out:
+  ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_bus_name_watcher_new:
+ * @bus_type: A #GMessageBusType specifying what message bus to connect to.
+ * @name: A bus name (well-known or unique).
+ * @cancellable: %NULL or a #GCancellable.
+ * @callback: The callback function to invoke when finished acquiring the name.
+ * @user_data: User data to pass to @callback.
+ *
+ * Creates a new #GBusNameWatcher and then check if someone owns @name
+ * on the bus specified by @bus_type. When the check is finished,
+ * @callback will be invoked (on the main thread) and you can use
+ * g_bus_name_watcher_new_finish() to get the result.
+ *
+ * Note that the returned object may be shared with other callers if
+ * @bus_type and @name matches.
+ *
+ * This class is the preferred way to implement D-Bus clients.
+ * The canonical example follows.
+ * <example><title>Using g_bus_name_watcher_new()</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; parse="text" href="../../../../gdbus/example-gbusnamewatcher.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
+ *
+ * Returns: A #GBusNameWatcher object. Free with g_object_unref().
+ **/
+GBusNameWatcher *
+g_bus_name_watcher_new (GMessageBusType        bus_type,
+                        const gchar           *name,
+                        GCancellable          *cancellable,
+                        GAsyncReadyCallback    callback,
+                        gpointer               user_data)
+{
+  GBusNameWatcher *watcher;
+  GDBusConnection *connection;
+  ConstructData *data;
+  GSimpleAsyncResult *simple;
+
+  /* This is all a tad complicated since we support singletons... the problem here
+   * in a nutshell is that there's a window, an initialization phase, where we wait
+   * for two things
+   *
+   *  - the connection for the bus to come up
+   *  - HasNameOwner() to return
+   *
+   * Now, suppose the user creates two watcher objects with the same bus and name
+   * and no bus is up. The second one will just be a reference to the first one
+   * because we support singletons. So basically while we are initializing, just
+   * add the callback for the second watcher to a list on the first one. Then
+   * when initialization is done, fire all callbacks.
+   */
+
+  data = g_new0 (ConstructData, 1);
+
+  connection = g_dbus_connection_bus_get (bus_type,
+                                          cancellable,
+                                          (GAsyncReadyCallback) get_connection_cb,
+                                          data);
+
+  G_LOCK (watcher_lock);
+  /* check if a suitable watcher already exists */
+  watcher = singletons_get_watcher (connection, name);
+  if (watcher != NULL)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (watcher),
+                                          callback,
+                                          user_data,
+                                          g_bus_name_watcher_new_finish);
+
+      if (watcher->priv->in_init_phase)
+        {
+          /* if still initializing, defer callback until we know what stuff looks like */
+          g_ptr_array_add (watcher->priv->init_phase_pending_simple_async_results, simple);
+        }
+      else
+        {
+          /* otherwise, return the result right away (e.g. in idle) */
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+      g_object_ref (watcher);
+      goto out;
+    }
+
+  watcher = G_BUS_NAME_WATCHER (g_object_new (G_TYPE_BUS_NAME_WATCHER,
+                                              "name", name,
+                                              "connection", connection,
+                                              NULL));
+
+  data->watcher = watcher;
+  watcher->priv->in_init_phase = TRUE;
+  watcher->priv->init_phase_pending_simple_async_results = g_ptr_array_new ();
+
+  simple = g_simple_async_result_new (G_OBJECT (watcher),
+                                      callback,
+                                      user_data,
+                                      g_bus_name_watcher_new_finish);
+
+  g_ptr_array_add (watcher->priv->init_phase_pending_simple_async_results, simple);
+
+  singletons_add_watcher (watcher);
+
+ out:
+  G_UNLOCK (watcher_lock);
+
+  g_object_unref (connection);
+
+  return watcher;
+}
+
+/**
+ * g_bus_name_watcher_new_finish:
+ * @watcher: A #GBusNameWatcher.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed to g_bus_name_watcher_get().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes attempting to determine the owner of the name being
+ * watched.
+ *
+ * Returns: %TRUE if the name being watched has an owner or %FALSE if
+ * there is no name owner or @error is set.
+ **/
+gboolean
+g_bus_name_watcher_new_finish (GBusNameWatcher       *watcher,
+                               GAsyncResult          *res,
+                               GError               **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  g_return_val_if_fail (G_IS_BUS_NAME_WATCHER (watcher), FALSE);
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_bus_name_watcher_new_finish);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return watcher->priv->name_owner != NULL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_bus_name_watcher_get_name:
+ * @watcher: A #GBusNameWatcher.
+ *
+ * Gets the name that @watcher is watching.
+ *
+ * Returns: The name that @watcher is watching. Do not free this
+ * string, it is owned by @watcher.
+ **/
+const gchar *
+g_bus_name_watcher_get_name (GBusNameWatcher *watcher)
+{
+  g_return_val_if_fail (G_IS_BUS_NAME_WATCHER (watcher), NULL);
+
+  return watcher->priv->name;
+}
+
+/**
+ * g_bus_name_watcher_get_name_owner:
+ * @watcher: A #GBusNameWatcher.
+ *
+ * Gets the unique name of the owner for the name that @watcher is watching, if any.
+ *
+ * To listen for changes, connect to the #GBusNameWatcher::name-appeared
+ * and #GBusNameWatcher::name-vanished signals or listen for notifications
+ * on the #GBusNameWatcher:name-owner property.
+ *
+ * Returns: The unique name for the owner of the name that @watcher is
+ * watching or %NULL if there is no owner. Do not free this string, it
+ * is owned by @watcher.
+ **/
+const gchar *
+g_bus_name_watcher_get_name_owner (GBusNameWatcher *watcher)
+{
+  g_return_val_if_fail (G_IS_BUS_NAME_WATCHER (watcher), NULL);
+
+  return watcher->priv->name_owner;
+}
+
+/**
+ * g_bus_name_watcher_get_connection:
+ * @watcher: A #GBusNameWatcher.
+ *
+ * Gets the #GDBusConnection used for @watcher.
+ *
+ * Returns: A #GDBusConnection object owned by @watcher. Do not unref.
+ **/
+GDBusConnection *
+g_bus_name_watcher_get_connection (GBusNameWatcher *watcher)
+{
+  g_return_val_if_fail (G_IS_BUS_NAME_WATCHER (watcher), NULL);
+
+  return watcher->priv->connection;
+}
+
+#define __G_BUS_NAME_WATCHER_C__
+#include "gdbusaliasdef.c"
diff --git a/gdbus/gbusnamewatcher.h b/gdbus/gbusnamewatcher.h
new file mode 100644
index 0000000..544f988
--- /dev/null
+++ b/gdbus/gbusnamewatcher.h
@@ -0,0 +1,113 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#if !defined (__G_DBUS_G_DBUS_H_INSIDE__) && !defined (G_DBUS_COMPILATION)
+#error "Only <gdbus/gdbus.h> can be included directly."
+#endif
+
+#ifndef __G_BUS_NAME_WATCHER_H__
+#define __G_BUS_NAME_WATCHER_H__
+
+#include <gdbus/gdbustypes.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_BUS_NAME_WATCHER         (g_bus_name_watcher_get_type ())
+#define G_BUS_NAME_WATCHER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BUS_NAME_WATCHER, GBusNameWatcher))
+#define G_BUS_NAME_WATCHER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BUS_NAME_WATCHER, GBusNameWatcherClass))
+#define G_BUS_NAME_WATCHER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BUS_NAME_WATCHER, GBusNameWatcherClass))
+#define G_IS_BUS_NAME_WATCHER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BUS_NAME_WATCHER))
+#define G_IS_BUS_NAME_WATCHER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BUS_NAME_WATCHER))
+
+typedef struct _GBusNameWatcherClass   GBusNameWatcherClass;
+typedef struct _GBusNameWatcherPrivate GBusNameWatcherPrivate;
+
+/**
+ * GBusNameWatcher:
+ *
+ * The #GBusNameWatcher structure contains only private data and
+ * should only be accessed using the provided API.
+ */
+struct _GBusNameWatcher
+{
+  /*< private >*/
+  GObject parent_instance;
+  GBusNameWatcherPrivate *priv;
+};
+
+/**
+ * GBusNameWatcherClass:
+ * @name_appeared: Signal class handler for the #GBusNameWatcher::name-appeared signal.
+ * @name_vanished: Signal class handler for the #GBusNameWatcher::name-vanished signal.
+ *
+ * Class structure for #GBusNameWatcher.
+ */
+struct _GBusNameWatcherClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /*< public >*/
+
+  /* Signals */
+  void (*name_appeared) (GBusNameWatcher *watcher);
+  void (*name_vanished) (GBusNameWatcher *watcher);
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+};
+
+GType               g_bus_name_watcher_get_type                  (void) G_GNUC_CONST;
+GBusNameWatcher    *g_bus_name_watcher_new                       (GMessageBusType        bus_type,
+                                                                  const gchar           *name,
+                                                                  GCancellable          *cancellable,
+                                                                  GAsyncReadyCallback    callback,
+                                                                  gpointer               user_data);
+gboolean            g_bus_name_watcher_new_finish                (GBusNameWatcher       *watcher,
+                                                                  GAsyncResult          *res,
+                                                                  GError               **error);
+#if 0
+GBusNameWatcher    *g_bus_name_watcher_new_for_connection        (GDBusConnection       *connection,
+                                                                  const gchar           *name,
+                                                                  GCancellable          *cancellable,
+                                                                  GAsyncReadyCallback    callback,
+                                                                  gpointer               user_data);
+gboolean            g_bus_name_watcher_new_for_connection_finish (GBusNameWatcher       *watcher,
+                                                                  GAsyncResult          *res,
+                                                                  GError               **error);
+#endif
+const gchar        *g_bus_name_watcher_get_name                  (GBusNameWatcher       *watcher);
+const gchar        *g_bus_name_watcher_get_name_owner            (GBusNameWatcher       *watcher);
+GDBusConnection    *g_bus_name_watcher_get_connection            (GBusNameWatcher       *watcher);
+
+G_END_DECLS
+
+#endif /* __G_BUS_NAME_WATCHER_H__ */
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 6abebc3..334f5a5 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -31,6 +31,7 @@
 #include <gdbus/gdbusconnection.h>
 #include <gdbus/gdbuserror.h>
 #include <gdbus/gbusnameowner.h>
+#include <gdbus/gbusnamewatcher.h>
 
 #undef __G_DBUS_D_DBUS_H_INSIDE__
 
diff --git a/gdbus/gdbus.symbols b/gdbus/gdbus.symbols
index cf10bc3..018250c 100644
--- a/gdbus/gdbus.symbols
+++ b/gdbus/gdbus.symbols
@@ -43,6 +43,17 @@ g_bus_name_owner_get_type
 #endif
 #endif
 
+#if IN_HEADER(__G_BUS_NAME_WATCHER_H__)
+#if IN_FILE(__G_BUS_NAME_WATCHER_C__)
+g_bus_name_watcher_new
+g_bus_name_watcher_new_finish
+g_bus_name_watcher_get_connection
+g_bus_name_watcher_get_name
+g_bus_name_watcher_get_name_owner
+g_bus_name_watcher_get_type
+#endif
+#endif
+
 #if IN_HEADER(__G_DBUS_MAINLOOP_H__)
 #if IN_FILE(__G_DBUS_MAINLOOP_C__)
 g_dbus_integrate_dbus_1_connection
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index a461862..5c4b5a3 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -60,6 +60,10 @@ struct _GDBusConnectionPrivate
   gboolean exit_on_close;
 
   gboolean is_private;
+
+  /* these variables are used to store async callbacks for users calling _new()  */
+  gboolean   in_init_phase;
+  GPtrArray *init_phase_pending_simple_async_results;
 };
 
 enum
@@ -341,6 +345,7 @@ static void
 g_dbus_connection_init (GDBusConnection *connection)
 {
   connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, G_TYPE_DBUS_CONNECTION, GDBusConnectionPrivate);
+  connection->priv->in_init_phase = TRUE;
 }
 
 /**
@@ -429,8 +434,9 @@ attempt_connect (GDBusConnection  *connection,
     }
   else
     {
-      dbus_1_connection = dbus_bus_get (connection->priv->bus_type,
-                                        &dbus_error);
+      /* for now, get a private bus even if it's shared */
+      dbus_1_connection = dbus_bus_get_private (connection->priv->bus_type,
+                                                &dbus_error);
     }
 
   if (dbus_1_connection != NULL)
@@ -573,6 +579,11 @@ g_dbus_connection_set_dbus_1_connection (GDBusConnection *connection,
         {
           dbus_connection_close (connection->priv->dbus_1_connection);
         }
+      else
+        {
+          /* see attempt_connext() where we still get a private bus even for shared stuff */
+          dbus_connection_close (connection->priv->dbus_1_connection);
+        }
       dbus_connection_unref (connection->priv->dbus_1_connection);
     }
 
@@ -600,24 +611,43 @@ g_dbus_connection_set_dbus_1_connection (GDBusConnection *connection,
 static gboolean
 open_bus_connection (gpointer user_data)
 {
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-  GDBusConnection *connection;
+  GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
   GError *error;
-
-  connection = G_DBUS_CONNECTION (g_simple_async_result_get_op_res_gpointer (simple));
+  GSimpleAsyncResult *simple;
+  guint n;
+  gboolean did_connect;
 
   error = NULL;
-  if (!attempt_connect (connection, &error))
+  did_connect = attempt_connect (connection, &error);
+
+  G_LOCK (connection_lock);
+  if (!did_connect)
     {
-      g_simple_async_result_set_from_error (simple, error);
+      for (n = 0; n < connection->priv->init_phase_pending_simple_async_results->len; n++)
+        {
+          simple = G_SIMPLE_ASYNC_RESULT (connection->priv->init_phase_pending_simple_async_results->pdata[n]);
+          g_simple_async_result_set_from_error (simple, error);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+
       g_error_free (error);
 
       /* set up timer for attempting to reconnect */
       setup_reconnect_timer (connection);
     }
-
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
+  else
+    {
+      for (n = 0; n < connection->priv->init_phase_pending_simple_async_results->len; n++)
+        {
+          simple = G_SIMPLE_ASYNC_RESULT (connection->priv->init_phase_pending_simple_async_results->pdata[n]);
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+        }
+    }
+  connection->priv->in_init_phase = FALSE;
+  g_ptr_array_free (connection->priv->init_phase_pending_simple_async_results, TRUE);
+  G_UNLOCK (connection_lock);
 
   return FALSE;
 }
@@ -632,6 +662,18 @@ g_dbus_connection_bus_get_internal (GMessageBusType                bus_type,
   GDBusConnection *connection;
   GSimpleAsyncResult *simple;
 
+  /* The fact we support singletons makes this complicated... since there's
+   * a window, the initialization phase, where we wait for the connection
+   * to be established.
+   *
+   * For example, suppose that two calls to g_dbus_connection_new() is
+   * made for the same bus when the program starts up. The we return
+   * two identical objects (since we support singletons) but the
+   * GAsyncReadyCallback are distinct. Hence, when returning a reffed
+   * instance of the singleton, we need to add the callback to the
+   * singleton so callbacks are fired at the right time.
+   */
+
   g_return_val_if_fail (bus_type != G_MESSAGE_BUS_TYPE_NONE, NULL);
 
   connection = NULL;
@@ -662,9 +704,9 @@ g_dbus_connection_bus_get_internal (GMessageBusType                bus_type,
     }
 
   /* singleton handling */
+  G_LOCK (connection_lock);
   if (!private)
     {
-      G_LOCK (connection_lock);
       if (bus_type == G_MESSAGE_BUS_TYPE_SESSION && the_session_bus != NULL)
         {
           connection = g_object_ref (the_session_bus);
@@ -672,8 +714,17 @@ g_dbus_connection_bus_get_internal (GMessageBusType                bus_type,
                                               callback,
                                               user_data,
                                               g_dbus_connection_bus_get_finish);
-          g_simple_async_result_complete_in_idle (simple);
-          g_object_unref (simple);
+          if (connection->priv->in_init_phase)
+            {
+              /* if still initializing, defer callback until we know what stuff looks like */
+              g_ptr_array_add (connection->priv->init_phase_pending_simple_async_results, simple);
+            }
+          else
+            {
+              /* otherwise, return the result right away (e.g. in idle) */
+              g_simple_async_result_complete_in_idle (simple);
+              g_object_unref (simple);
+            }
           G_UNLOCK (connection_lock);
           goto out;
         }
@@ -684,8 +735,17 @@ g_dbus_connection_bus_get_internal (GMessageBusType                bus_type,
                                               callback,
                                               user_data,
                                               g_dbus_connection_bus_get_finish);
-          g_simple_async_result_complete_in_idle (simple);
-          g_object_unref (simple);
+          if (connection->priv->in_init_phase)
+            {
+              /* if still initializing, defer callback until we know what stuff looks like */
+              g_ptr_array_add (connection->priv->init_phase_pending_simple_async_results, simple);
+            }
+          else
+            {
+              /* otherwise, return the result right away (e.g. in idle) */
+              g_simple_async_result_complete_in_idle (simple);
+              g_object_unref (simple);
+            }
           G_UNLOCK (connection_lock);
           goto out;
         }
@@ -714,11 +774,13 @@ g_dbus_connection_bus_get_internal (GMessageBusType                bus_type,
                                       callback,
                                       user_data,
                                       private ? g_dbus_connection_bus_get_private_finish : g_dbus_connection_bus_get_finish);
-  g_simple_async_result_set_op_res_gpointer (simple,
-                                             connection,
-                                             NULL);
+  connection->priv->init_phase_pending_simple_async_results = g_ptr_array_new ();
+  g_ptr_array_add (connection->priv->init_phase_pending_simple_async_results, simple);
+  G_UNLOCK (connection_lock);
+
   g_idle_add (open_bus_connection,
-              simple);
+              connection);
+
 
  out:
   return connection;
@@ -875,7 +937,10 @@ g_dbus_connection_bus_get_finish (GDBusConnection *connection,
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_connection_bus_get_finish);
-  return !g_simple_async_result_propagate_error (simple, error);
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+  else
+    return g_dbus_connection_get_is_open (connection);
 }
 
 /**
@@ -896,7 +961,10 @@ g_dbus_connection_bus_get_private_finish (GDBusConnection *connection,
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_connection_bus_get_private_finish);
-  return !g_simple_async_result_propagate_error (simple, error);
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+  else
+    return g_dbus_connection_get_is_open (connection);
 }
 
 /**
diff --git a/gdbus/gdbustypes.h b/gdbus/gdbustypes.h
index 21dacb2..508613e 100644
--- a/gdbus/gdbustypes.h
+++ b/gdbus/gdbustypes.h
@@ -33,6 +33,7 @@ G_BEGIN_DECLS
 
 typedef struct _GDBusConnection       GDBusConnection;
 typedef struct _GBusNameOwner         GBusNameOwner;
+typedef struct _GBusNameWatcher       GBusNameWatcher;
 
 G_END_DECLS
 
diff --git a/gdbus/tests/connection.c b/gdbus/tests/connection.c
index 58f08c2..05118f9 100644
--- a/gdbus/tests/connection.c
+++ b/gdbus/tests/connection.c
@@ -30,9 +30,39 @@
 #include <signal.h>
 #include <stdio.h>
 
+/* TODO: clean up and move to gtestutils.c
+ *
+ * This is needed because libdbus-1 does not give predictable error messages - e.g. you
+ * get a different error message on connecting to a bus if the socket file is there vs
+ * if the socket file is missing.
+ */
+#define _g_assert_error_domain(err, dom)	do { if (!err || (err)->domain != dom) \
+      g_assertion_message_error (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+                                 #err, err, dom, -1); } while (0)
+
 /* all tests rely on a shared mainloop */
 static GMainLoop *loop = NULL;
 
+#if 0
+static gboolean
+wakeup_mainloop (gpointer user_data)
+{
+  GMainLoop *sleep_loop = user_data;
+  g_main_loop_quit (sleep_loop);
+  return FALSE;
+}
+
+static void
+sleep_in_mainloop (guint msec)
+{
+  GMainLoop *sleep_loop;
+  sleep_loop = g_main_loop_new (NULL, FALSE);
+  g_timeout_add (msec, wakeup_mainloop, sleep_loop);
+  g_main_loop_run (sleep_loop);
+  g_main_loop_unref (sleep_loop);
+}
+#endif
+
 /* ---------------------------------------------------------------------------------------------------- */
 /* Utilities for bringing up and tearing down session message bus instances */
 
@@ -367,7 +397,7 @@ get_connection_callback_no_bus_exists (GDBusConnection *connection,
   ret = g_dbus_connection_bus_get_finish (connection,
                                           result,
                                           &error);
-  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_NOT_FOUND);
+  _g_assert_error_domain (error, G_DBUS_ERROR);
   g_error_free (error);
   g_assert (!ret);
   g_main_loop_quit (loop);
@@ -464,11 +494,37 @@ get_connection_callback_private (GDBusConnection *connection,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+get_connection_callback_no_bus_exists_singleton (GDBusConnection *connection,
+                                                 GAsyncResult    *result,
+                                                 gpointer         user_data)
+{
+  guint *counter = user_data;
+  GError *error;
+  gboolean ret;
+
+  error = NULL;
+  ret = g_dbus_connection_bus_get_finish (connection,
+                                          result,
+                                          &error);
+  _g_assert_error_domain (error, G_DBUS_ERROR);
+  g_error_free (error);
+  g_assert (!ret);
+
+  *counter += 1;
+
+  if (*counter == 2)
+    g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
 test_message_bus_connections (void)
 {
   GDBusConnection *c;
   GDBusConnection *c2;
   gboolean val;
+  guint counter;
 
   /* Check for correct behavior when no bus is present */
   c = g_dbus_connection_bus_get (G_MESSAGE_BUS_TYPE_SESSION,
@@ -548,6 +604,25 @@ test_message_bus_connections (void)
   g_assert (!val);
 
   g_object_unref (c);
+
+  /* Check that the error paths are correct when using singletons */
+  counter = 0;
+  c = g_dbus_connection_bus_get (G_MESSAGE_BUS_TYPE_SESSION,
+                                 NULL,
+                                 (GAsyncReadyCallback) get_connection_callback_no_bus_exists_singleton,
+                                 &counter);
+  c2 = g_dbus_connection_bus_get (G_MESSAGE_BUS_TYPE_SESSION,
+                                 NULL,
+                                 (GAsyncReadyCallback) get_connection_callback_no_bus_exists_singleton,
+                                 &counter);
+  g_assert (!g_dbus_connection_get_is_open (c));
+  g_assert (!g_dbus_connection_get_is_open (c2));
+  g_main_loop_run (loop);
+  g_assert (counter == 2);
+  g_assert (!g_dbus_connection_get_is_open (c));
+  g_assert (!g_dbus_connection_get_is_open (c2));
+  g_object_unref (c);
+  g_object_unref (c2);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -581,7 +656,7 @@ on_owner_cb_expect_failure (GBusNameOwner *owner,
   ret = g_bus_name_owner_new_finish (owner,
                                      result,
                                      &error);
-  g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_NOT_FOUND);
+  _g_assert_error_domain (error, G_DBUS_ERROR);
   g_error_free (error);
   g_assert (!ret);
 
@@ -874,6 +949,9 @@ test_bus_name_owner (void)
       g_assert (val);
       g_assert (!g_bus_name_owner_get_owns_name (o2));
     }
+  /* make sure we don't exit when bringing down the bus */
+  g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o), FALSE);
+  g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o2), FALSE);
   g_object_unref (o2);
   g_object_unref (o);
 
@@ -881,6 +959,246 @@ test_bus_name_owner (void)
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
+/* Test that GBusNameWatcher works correctly */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_error (GBusNameWatcher *watcher,
+                     GAsyncResult    *res,
+                     gpointer         user_data)
+{
+  GError *error;
+  gboolean has_name_owner;
+
+  error = NULL;
+  has_name_owner = g_bus_name_watcher_new_finish (watcher,
+                                                  res,
+                                                  &error);
+  _g_assert_error_domain (error, G_DBUS_ERROR);
+  g_error_free (error);
+  g_assert (!has_name_owner);
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_null_owner (GBusNameWatcher *watcher,
+                          GAsyncResult    *res,
+                          gpointer         user_data)
+{
+  GError *error;
+  gboolean has_name_owner;
+
+  error = NULL;
+  has_name_owner = g_bus_name_watcher_new_finish (watcher,
+                                                  res,
+                                                  &error);
+  g_assert_no_error (error);
+  g_assert (!has_name_owner);
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+nbw_on_owner_cb_expect_success (GBusNameOwner *owner,
+                                GAsyncResult  *result,
+                                gpointer       user_data)
+{
+  gboolean ret;
+  GError *error;
+
+  error = NULL;
+  ret = g_bus_name_owner_new_finish (owner,
+                                     result,
+                                     &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_owner (GBusNameWatcher *watcher,
+                     GAsyncResult    *res,
+                     gpointer         user_data)
+{
+  GError *error;
+  gboolean has_name_owner;
+
+  error = NULL;
+  has_name_owner = g_bus_name_watcher_new_finish (watcher,
+                                                  res,
+                                                  &error);
+  g_assert_no_error (error);
+  g_assert (has_name_owner);
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_on_name_vanished (GBusNameWatcher *watcher,
+                      gpointer         user_data)
+{
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_on_name_appeared (GBusNameWatcher *watcher,
+                      gpointer         user_data)
+{
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_singleton (GBusNameWatcher *watcher,
+                  GAsyncResult    *res,
+                  gpointer         user_data)
+{
+  gboolean *val = user_data;
+  GError *error;
+  gboolean has_name_owner;
+
+  error = NULL;
+  has_name_owner = g_bus_name_watcher_new_finish (watcher,
+                                                  res,
+                                                  &error);
+  g_assert_no_error (error);
+  g_assert (has_name_owner);
+
+  *val = TRUE;
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_bus_name_watcher (void)
+{
+  GBusNameWatcher *w;
+  GBusNameWatcher *w2;
+  GBusNameOwner *o;
+  gboolean val;
+
+  /* if no session bus is available, expect the callback to fail */
+  w = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+                              "org.gtk.Test.Name1",
+                              NULL,
+                              (GAsyncReadyCallback) bnw_cb_expect_error,
+                              NULL);
+  g_main_loop_run (loop);
+  g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
+  g_object_unref (w);
+
+  /* if a session bus is available, expect the callback to succeed with name_owner==NULL */
+  session_bus_up ();
+  w = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+                              "org.gtk.Test.Name1",
+                              NULL,
+                              (GAsyncReadyCallback) bnw_cb_expect_null_owner,
+                              NULL);
+  g_main_loop_run (loop);
+  g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
+  g_object_unref (w);
+
+  /* now start owning a name */
+  o = g_bus_name_owner_new (G_MESSAGE_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                            NULL,
+                            (GAsyncReadyCallback) nbw_on_owner_cb_expect_success,
+                            NULL);
+  g_main_loop_run (loop);
+
+  /* now we should expect to get the name owner in the callback when constructing the watcher */
+  w = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+                              "org.gtk.Test.Name1",
+                              NULL,
+                              (GAsyncReadyCallback) bnw_cb_expect_owner,
+                              NULL);
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+
+  /* now stop owning the name and check that the watcher emits the ::name-vanished signal */
+  g_signal_connect (w, "name-vanished", G_CALLBACK (bnw_on_name_vanished), NULL);
+  g_object_unref (o);
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), ==, NULL);
+  g_signal_handlers_disconnect_by_func (w, bnw_on_name_vanished, NULL);
+
+  /* start owning the name and check that the watcher emits the ::name-appeared signal */
+  g_signal_connect (w, "name-appeared", G_CALLBACK (bnw_on_name_appeared), NULL);
+  o = g_bus_name_owner_new (G_MESSAGE_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                            NULL,
+                            NULL,
+                            NULL);
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+  g_signal_handlers_disconnect_by_func (w, bnw_on_name_appeared, NULL);
+
+  /* now check that we get ::name-vanished when the bus goes away */
+  g_signal_connect (w, "name-vanished", G_CALLBACK (bnw_on_name_vanished), NULL);
+  session_bus_down ();
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), ==, NULL);
+  g_signal_handlers_disconnect_by_func (w, bnw_on_name_vanished, NULL);
+
+  /* and ::name-appeared when the bus comes back up (this works because
+   * GBusNameOwner will claim the name on reconnection)
+   */
+  g_signal_connect (w, "name-appeared", G_CALLBACK (bnw_on_name_appeared), NULL);
+  session_bus_up ();
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+  g_signal_handlers_disconnect_by_func (w, bnw_on_name_appeared, NULL);
+
+  /* check that singleton handling works, e.g. a new watcher for the _same_
+   * connection and name gives the same object
+   */
+  val = FALSE;
+  w2 = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+                               "org.gtk.Test.Name1",
+                               NULL,
+                               (GAsyncReadyCallback) bnw_cb_singleton,
+                               &val);
+  g_assert (w == w2);
+  g_assert (!val);
+  g_main_loop_run (loop);
+  g_assert (val);
+  g_object_unref (w2);
+  w2 = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+                               "org.gtk.Test.Name2",
+                               NULL,
+                               NULL,
+                               NULL);
+  g_assert (w != w2);
+  g_object_unref (w2);
+
+  /* stop watching */
+  g_object_unref (w);
+
+  /* kill owner */
+  g_object_unref (o);
+
+  /* tear down bus */
+  session_bus_down ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
 
 int
 main (int   argc,
@@ -900,5 +1218,6 @@ main (int   argc,
 
   g_test_add_func ("/gdbus/message-bus-connections", test_message_bus_connections);
   g_test_add_func ("/gdbus/bus-name-owner", test_bus_name_owner);
+  g_test_add_func ("/gdbus/bus-name-watcher", test_bus_name_watcher);
   return g_test_run();
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]