[telepathy-glib/telepathy-glib-0.8] TpConnectionManager: defer ListProtocols() until we know the initial name

Simon McVittie simon.mcvittie at collabora.co.uk
Wed Oct 14 14:14:03 PDT 2009


The fact that a name owner change to "" ends introspection and emits
"exited" means we don't behave as documented in
tp_connection_manager_activate, which claims that "exited" before
"activated" means a failure to activate.

This isn't necessarily true if someone calls
tp_connection_manager_activate() straight after constructing the TpCM,
*and* the CM is not currently running. In that case, the ListProtocols()
call is in parallel with GetNameOwner(); GetNameOwner() will win the
race, because dbus-daemon processes it synchronously and replies
immediately. When GetNameOwner() returns error, watch_name_owner tells us
there is no owner, the ListProtocols() call is cancelled, and "exited"
is emitted.

However, that ListProtocols() call was in fact a perfectly good
service-activation, which would start the CM up!

The solution is to defer ListProtocols() until the CM's initial name is
known, and avoid emitting "exited" on the basis of initial state.
---
 telepathy-glib/connection-manager.c |   80 ++++++++++++++++++++++++-----------
 1 files changed, 55 insertions(+), 25 deletions(-)

diff --git a/telepathy-glib/connection-manager.c b/telepathy-glib/connection-manager.c
index 5f8235f..193e3a0 100644
--- a/telepathy-glib/connection-manager.c
+++ b/telepathy-glib/connection-manager.c
@@ -181,9 +181,6 @@ struct _TpConnectionManagerPrivate {
     /* source ID for introspecting later */
     guint introspect_idle_id;
 
-    /* FALSE if constructor hasn't run yet */
-    unsigned constructed:1;
-
     /* TRUE if dispose() has run already */
     unsigned disposed:1;
 
@@ -211,6 +208,12 @@ struct _TpConnectionManagerPrivate {
 
     /* the method call currently pending, or NULL if none. */
     TpProxyPendingCall *introspection_call;
+
+    /* FALSE if initial name-owner (if any) hasn't been found yet */
+    gboolean name_known;
+    /* TRUE if someone asked us to activate but we're putting it off until
+     * name_known */
+    gboolean want_activation;
 };
 
 G_DEFINE_TYPE (TpConnectionManager,
@@ -656,6 +659,8 @@ tp_connection_manager_idle_introspect (gpointer data)
   return FALSE;
 }
 
+static gboolean tp_connection_manager_idle_read_manager_file (gpointer data);
+
 static void
 tp_connection_manager_name_owner_changed_cb (TpDBusDaemon *bus,
                                              const gchar *name,
@@ -678,7 +683,12 @@ tp_connection_manager_name_owner_changed_cb (TpDBusDaemon *bus,
       if (introspection_in_progress (self))
         tp_connection_manager_end_introspection (self, &e);
 
-      g_signal_emit (self, signals[SIGNAL_EXITED], 0);
+      /* If our name wasn't known already, a change to "" is just the initial
+       * state, so we didn't *exit* as such. */
+      if (self->priv->name_known)
+        {
+          g_signal_emit (self, signals[SIGNAL_EXITED], 0);
+        }
     }
   else
     {
@@ -695,6 +705,27 @@ tp_connection_manager_name_owner_changed_cb (TpDBusDaemon *bus,
             tp_connection_manager_idle_introspect, self);
     }
 
+  /* if we haven't started introspecting yet, now would be a good time */
+  if (!self->priv->name_known)
+    {
+      g_assert (self->priv->manager_file_read_idle_id == 0);
+
+      /* now we know whether we're running or not, we can try reading the
+       * .manager file... */
+      self->priv->manager_file_read_idle_id = g_idle_add (
+          tp_connection_manager_idle_read_manager_file, self);
+
+      if (self->priv->want_activation && self->priv->introspect_idle_id == 0)
+        {
+          /* ... but if activation was requested, we should also do that */
+          self->priv->introspect_idle_id = g_idle_add (
+              tp_connection_manager_idle_introspect, self);
+        }
+
+      /* Unfreeze automatic reading of .manager file if manager-file changes */
+      self->priv->name_known = TRUE;
+    }
+
   g_object_unref (self);
 }
 
@@ -1232,14 +1263,6 @@ tp_connection_manager_constructor (GType type,
           tp_connection_manager_find_manager_file (self->name);
     }
 
-  g_assert (self->priv->manager_file_read_idle_id == 0);
-
-  self->priv->manager_file_read_idle_id = g_idle_add (
-      tp_connection_manager_idle_read_manager_file, self);
-
-  /* Unfreeze automatic reading of .manager file if manager-file changes */
-  self->priv->constructed = TRUE;
-
   return (GObject *) self;
 }
 
@@ -1349,12 +1372,12 @@ tp_connection_manager_set_property (GObject *object,
     case PROP_MANAGER_FILE:
       g_free (self->priv->manager_file);
 
-      /* If the constructor has already run, change the definition of where
+      /* If initial code has already run, change the definition of where
        * we expect to find the .manager file and trigger re-introspection.
-       * Otherwise, just take the value - _constructor queues the first-time
-       * manager file lookup anyway.
+       * Otherwise, just take the value - when name_known becomes TRUE we
+       * queue the first-time manager file lookup anyway.
        */
-      if (self->priv->constructed)
+      if (self->priv->name_known)
         {
           const gchar *tmp = g_value_get_string (value);
 
@@ -1634,17 +1657,24 @@ tp_connection_manager_new (TpDBusDaemon *dbus,
 gboolean
 tp_connection_manager_activate (TpConnectionManager *self)
 {
-  if (self->running || introspection_in_progress (self))
+  if (self->priv->name_known)
     {
-      DEBUG ("already %s", self->running ? "running" : "introspecting");
-      return FALSE;
-    }
-
-  DEBUG ("calling ListProtocols");
+      if (self->running)
+        {
+          DEBUG ("already running");
+          return FALSE;
+        }
 
-  self->priv->introspection_call =
-    tp_cli_connection_manager_call_list_protocols (self, -1,
-        tp_connection_manager_got_protocols, NULL, NULL, NULL);
+      if (self->priv->introspect_idle_id == 0)
+        self->priv->introspect_idle_id = g_idle_add (
+            tp_connection_manager_idle_introspect, self);
+    }
+  else
+    {
+      /* we'll activate later, when we know properly whether we're running */
+      DEBUG ("queueing activation for when we know what's going on");
+      self->priv->want_activation = TRUE;
+    }
 
   return TRUE;
 }
-- 
1.5.6.5




More information about the telepathy-commits mailing list