[telepathy-glib/master] TpDBusDaemon: track NameOwnerChanged by using raw libdbus

Simon McVittie simon.mcvittie at collabora.co.uk
Sun Aug 16 10:15:37 PDT 2009


This avoids one of the points at which we make a DBusGProxy for the bus daemon.
---
 telepathy-glib/dbus.c |  180 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 167 insertions(+), 13 deletions(-)

diff --git a/telepathy-glib/dbus.c b/telepathy-glib/dbus.c
index 8f9b6e4..64d5311 100644
--- a/telepathy-glib/dbus.c
+++ b/telepathy-glib/dbus.c
@@ -71,6 +71,9 @@
 
 #include "telepathy-glib/_gen/tp-cli-dbus-daemon-body.h"
 
+#define DEBUG_FLAG TP_DEBUG_PROXY
+#include "debug-internal.h"
+
 /**
  * tp_asv_size:
  * @asv: a GHashTable
@@ -761,15 +764,88 @@ _tp_dbus_daemon_name_owner_changed (TpDBusDaemon *self,
   watch->callback (self, name, new_owner, watch->user_data);
 }
 
+static dbus_int32_t daemons_slot = -1;
+
+typedef struct {
+    DBusConnection *libdbus;
+    DBusMessage *message;
+} NOCIdleContext;
+
+static NOCIdleContext *
+noc_idle_context_new (DBusConnection *libdbus,
+                      DBusMessage *message)
+{
+  NOCIdleContext *context = g_slice_new (NOCIdleContext);
+
+  context->libdbus = dbus_connection_ref (libdbus);
+  context->message = dbus_message_ref (message);
+  return context;
+}
+
 static void
-_tp_dbus_daemon_name_owner_changed_cb (TpDBusDaemon *self,
-                                       const gchar *name,
-                                       const gchar *old_owner,
-                                       const gchar *new_owner,
-                                       gpointer user_data,
-                                       GObject *object)
+noc_idle_context_free (gpointer data)
 {
-  _tp_dbus_daemon_name_owner_changed (self, name, new_owner);
+  NOCIdleContext *context = data;
+
+  dbus_connection_unref (context->libdbus);
+  dbus_message_unref (context->message);
+  g_slice_free (NOCIdleContext, context);
+}
+
+static gboolean
+noc_idle_context_invoke (gpointer data)
+{
+  NOCIdleContext *context = data;
+  const gchar *name;
+  const gchar *old_owner;
+  const gchar *new_owner;
+  DBusError dbus_error = DBUS_ERROR_INIT;
+  GSList **daemons;
+
+  if (daemons_slot == -1)
+    return FALSE;
+
+  if (!dbus_message_get_args (context->message, &dbus_error,
+        DBUS_TYPE_STRING, &name,
+        DBUS_TYPE_STRING, &old_owner,
+        DBUS_TYPE_STRING, &new_owner,
+        DBUS_TYPE_INVALID))
+    {
+      DEBUG ("Couldn't unpack NameOwnerChanged(s, s, s): %s: %s",
+          dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      return FALSE;
+    }
+
+  daemons = dbus_connection_get_data (context->libdbus, daemons_slot);
+
+  /* should always be non-NULL, barring bugs */
+  if (G_LIKELY (daemons != NULL))
+    {
+      GSList *iter;
+
+      for (iter = *daemons; iter != NULL; iter = iter->next)
+        _tp_dbus_daemon_name_owner_changed (iter->data, name, new_owner);
+    }
+
+  return FALSE;
+}
+
+static DBusHandlerResult
+_tp_dbus_daemon_name_owner_changed_filter (DBusConnection *libdbus,
+                                           DBusMessage *message,
+                                           void *unused G_GNUC_UNUSED)
+{
+  /* We have to do the real work in an idle, so we don't break re-entrant
+   * calls (the dbus-glib event source isn't re-entrant) */
+  if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+        "NameOwnerChanged") &&
+      dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+    g_idle_add_full (G_PRIORITY_HIGH, noc_idle_context_invoke,
+        noc_idle_context_new (libdbus, message),
+        noc_idle_context_free);
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
 static void
@@ -800,6 +876,17 @@ _tp_dbus_daemon_got_name_owner (TpDBusDaemon *self,
  * Since: 0.7.1
  */
 
+static inline gchar *
+_tp_dbus_daemon_get_noc_rule (const gchar *name)
+{
+  return g_strdup_printf ("type='signal',"
+      "sender='" DBUS_SERVICE_DBUS "',"
+      "path='" DBUS_PATH_DBUS "',"
+      "interface='"DBUS_INTERFACE_DBUS "',"
+      "member='NameOwnerChanged',"
+      "arg0='%s'", name);
+}
+
 /**
  * tp_dbus_daemon_watch_name_owner:
  * @self: The D-Bus daemon
@@ -829,11 +916,15 @@ tp_dbus_daemon_watch_name_owner (TpDBusDaemon *self,
       name);
 
   g_return_if_fail (TP_IS_DBUS_DAEMON (self));
+  g_return_if_fail (tp_dbus_check_valid_bus_name (name,
+        TP_DBUS_NAME_TYPE_ANY, NULL));
   g_return_if_fail (name != NULL);
   g_return_if_fail (callback != NULL);
 
   if (watch == NULL)
     {
+      gchar *match_rule;
+
       /* Allocate a single watch (common case) */
       watch = g_slice_new (_NameOwnerWatch);
       watch->callback = callback;
@@ -844,6 +935,13 @@ tp_dbus_daemon_watch_name_owner (TpDBusDaemon *self,
       g_hash_table_insert (self->priv->name_owner_watches, g_strdup (name),
           watch);
 
+      /* We want to be notified about name owner changes for this one.
+       * Assume the match addition will succeed; there's no good way to cope
+       * with failure here... */
+      match_rule = _tp_dbus_daemon_get_noc_rule (name);
+      DEBUG ("Adding match rule %s", match_rule);
+      dbus_bus_add_match (self->priv->libdbus, match_rule, NULL);
+
       tp_cli_dbus_daemon_call_get_name_owner (self, -1, name,
           _tp_dbus_daemon_got_name_owner,
           g_strdup (name), g_free, NULL);
@@ -892,11 +990,18 @@ _tp_dbus_daemon_stop_watching (TpDBusDaemon *self,
                                const gchar *name,
                                _NameOwnerWatch *watch)
 {
+  gchar *match_rule;
+
   if (watch->destroy)
     watch->destroy (watch->user_data);
 
   g_free (watch->last_owner);
   g_slice_free (_NameOwnerWatch, watch);
+
+  match_rule = _tp_dbus_daemon_get_noc_rule (name);
+  DEBUG ("Removing match rule %s", match_rule);
+  dbus_bus_remove_match (self->priv->libdbus, match_rule, NULL);
+  g_free (match_rule);
 }
 
 /**
@@ -1222,6 +1327,15 @@ tp_dbus_daemon_release_name (TpDBusDaemon *self,
     }
 }
 
+static void
+free_daemon_list (gpointer p)
+{
+  GSList **slistp = p;
+
+  g_slist_free (*slistp);
+  g_slice_free (GSList *, slistp);
+}
+
 static GObject *
 tp_dbus_daemon_constructor (GType type,
                             guint n_params,
@@ -1232,12 +1346,7 @@ tp_dbus_daemon_constructor (GType type,
   TpDBusDaemon *self = TP_DBUS_DAEMON (object_class->constructor (type,
         n_params, params));
   TpProxy *as_proxy = (TpProxy *) self;
-
-  /* Connect to my own NameOwnerChanged signal.
-   * The proxy hasn't had a chance to become invalid yet, so we can
-   * assume that this signal connection will work */
-  tp_cli_dbus_daemon_connect_to_name_owner_changed (self,
-      _tp_dbus_daemon_name_owner_changed_cb, NULL, NULL, NULL, NULL);
+  GSList **daemons;
 
   g_assert (!tp_strdiff (as_proxy->bus_name, DBUS_SERVICE_DBUS));
   g_assert (!tp_strdiff (as_proxy->object_path, DBUS_PATH_DBUS));
@@ -1246,6 +1355,28 @@ tp_dbus_daemon_constructor (GType type,
       dbus_g_connection_get_connection (
         tp_proxy_get_dbus_connection (self)));
 
+  /* one ref per TpDBusDaemon, released in finalize */
+  if (!dbus_connection_allocate_data_slot (&daemons_slot))
+    g_error ("Out of memory");
+
+  daemons = dbus_connection_get_data (self->priv->libdbus, daemons_slot);
+
+  if (daemons == NULL)
+    {
+      daemons = g_slice_new (GSList *);
+
+      *daemons = NULL;
+      dbus_connection_set_data (self->priv->libdbus, daemons_slot, daemons,
+          free_daemon_list);
+
+      /* we add this filter at most once per DBusConnection */
+      if (!dbus_connection_add_filter (self->priv->libdbus,
+            _tp_dbus_daemon_name_owner_changed_filter, NULL, NULL))
+        g_error ("Out of memory");
+    }
+
+  *daemons = g_slist_prepend (*daemons, self);
+
   return (GObject *) self;
 }
 
@@ -1263,6 +1394,7 @@ static void
 tp_dbus_daemon_dispose (GObject *object)
 {
   TpDBusDaemon *self = TP_DBUS_DAEMON (object);
+  GSList **daemons;
 
   if (self->priv->name_owner_watches != NULL)
     {
@@ -1284,6 +1416,15 @@ tp_dbus_daemon_dispose (GObject *object)
 
   if (self->priv->libdbus != NULL)
     {
+      /* remove myself from the list to be notified on NoC */
+      daemons = dbus_connection_get_data (self->priv->libdbus, daemons_slot);
+
+      /* should always be non-NULL, barring bugs */
+      if (G_LIKELY (daemons != NULL))
+        {
+          *daemons = g_slist_remove (*daemons, self);
+        }
+
       dbus_connection_unref (self->priv->libdbus);
       self->priv->libdbus = NULL;
     }
@@ -1291,6 +1432,18 @@ tp_dbus_daemon_dispose (GObject *object)
   G_OBJECT_CLASS (tp_dbus_daemon_parent_class)->dispose (object);
 }
 
+static void
+tp_dbus_daemon_finalize (GObject *object)
+{
+  GObjectFinalizeFunc chain_up = G_OBJECT_CLASS (tp_dbus_daemon_parent_class)->finalize;
+
+  /* one ref per TpDBusDaemon, from constructor */
+  dbus_connection_free_data_slot (&daemons_slot);
+
+  if (chain_up != NULL)
+    chain_up (object);
+}
+
 /**
  * tp_dbus_daemon_init_known_interfaces:
  *
@@ -1330,6 +1483,7 @@ tp_dbus_daemon_class_init (TpDBusDaemonClass *klass)
 
   object_class->constructor = tp_dbus_daemon_constructor;
   object_class->dispose = tp_dbus_daemon_dispose;
+  object_class->finalize = tp_dbus_daemon_finalize;
 
   proxy_class->interface = TP_IFACE_QUARK_DBUS_DAEMON;
 }
-- 
1.5.6.5




More information about the telepathy-commits mailing list