telepathy-glib: TpConnectionManager: retry introspection after CM exits, up to once

Simon McVittie smcv at kemper.freedesktop.org
Tue Aug 20 13:03:42 PDT 2013


Module: telepathy-glib
Branch: master
Commit: 60120429b3fd85170cefd72cb913e7014bb98644
URL:    http://cgit.freedesktop.org/telepathy/telepathy-glib/commit/?id=60120429b3fd85170cefd72cb913e7014bb98644

Author: Simon McVittie <simon.mcvittie at collabora.co.uk>
Date:   Mon Jul 22 17:57:23 2013 +0100

TpConnectionManager: retry introspection after CM exits, up to once

Many connection managers automatically exit after 5 seconds of
inactivity. If the CM has no .manager file *and* exits in this way
while we are introspecting it, we would previously consider it to have
failed introspection - but with sufficiently unfortunate timing,
that can result in empathy-accounts not considering Haze to exist.

To avoid this, without going into an infinite loop if the CM fails to
introspect, retry once, but only once.

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=67183
Reviewed-by: Guillaume Desmottes <guillaume.desmottes at collabora.co.uk>

---

 telepathy-glib/connection-manager.c |   29 ++++++++++++-
 tests/dbus/cm.c                     |   76 +++++++++++++++++++++++++++++++---
 2 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/telepathy-glib/connection-manager.c b/telepathy-glib/connection-manager.c
index 7130f62..c816bfc 100644
--- a/telepathy-glib/connection-manager.c
+++ b/telepathy-glib/connection-manager.c
@@ -278,6 +278,9 @@ struct _TpConnectionManagerPrivate {
     /* TRUE if someone asked us to activate but we're putting it off until
      * name_known */
     gboolean want_activation;
+    /* TRUE if the CM exited (crashed?) during introspection.
+     * We'll retry, but only once. */
+    gboolean retried_introspection;
 };
 
 G_DEFINE_TYPE (TpConnectionManager,
@@ -623,8 +626,7 @@ static void tp_connection_manager_ready_or_failed (TpConnectionManager *self,
                                        const GError *error);
 
 static void
-tp_connection_manager_end_introspection (TpConnectionManager *self,
-                                         const GError *error)
+tp_connection_manager_reset_introspection (TpConnectionManager *self)
 {
   guint i;
 
@@ -651,6 +653,14 @@ tp_connection_manager_end_introspection (TpConnectionManager *self,
       self->priv->pending_protocols = NULL;
     }
 
+}
+
+static void
+tp_connection_manager_end_introspection (TpConnectionManager *self,
+                                         const GError *error)
+{
+  tp_connection_manager_reset_introspection (self);
+
   DEBUG ("End of introspection, info source %u", self->info_source);
   g_signal_emit (self, signals[SIGNAL_GOT_INFO], 0, self->info_source);
   tp_connection_manager_ready_or_failed (self, error);
@@ -930,7 +940,20 @@ tp_connection_manager_name_owner_changed_cb (TpDBusDaemon *bus,
 
       /* cancel pending introspection, if any */
       if (introspection_in_progress (self))
-        tp_connection_manager_end_introspection (self, &e);
+        {
+          if (self->priv->retried_introspection)
+            {
+              DEBUG ("%s, twice: assuming fatal and not retrying", e.message);
+              tp_connection_manager_end_introspection (self, &e);
+            }
+          else
+            {
+              self->priv->retried_introspection = TRUE;
+              DEBUG ("%s: retrying", e.message);
+              tp_connection_manager_reset_introspection (self);
+              tp_connection_manager_continue_introspection (self);
+            }
+        }
 
       /* If our name wasn't known already, a change to "" is just the initial
        * state, so we didn't *exit* as such. */
diff --git a/tests/dbus/cm.c b/tests/dbus/cm.c
index 363c1e7..86c7c1d 100644
--- a/tests/dbus/cm.c
+++ b/tests/dbus/cm.c
@@ -18,7 +18,9 @@
 typedef enum {
     ACTIVATE_CM = (1 << 0),
     USE_CWR = (1 << 1),
-    USE_OLD_LIST = (1 << 2)
+    USE_OLD_LIST = (1 << 2),
+    DROP_NAME_ON_GET = (1 << 3),
+    DROP_NAME_ON_GET_TWICE = (1 << 4)
 } TestFlags;
 
 typedef struct {
@@ -32,7 +34,10 @@ typedef struct {
     GError *error /* initialized where needed */;
 } Test;
 
-typedef TpTestsEchoConnectionManager PropertylessConnectionManager;
+typedef struct {
+    TpTestsEchoConnectionManager parent;
+    guint drop_name_on_get;
+} PropertylessConnectionManager;
 typedef TpTestsEchoConnectionManagerClass PropertylessConnectionManagerClass;
 
 static void stub_properties_iface_init (gpointer iface);
@@ -65,10 +70,30 @@ stub_get (TpSvcDBusProperties *iface G_GNUC_UNUSED,
 }
 
 static void
-stub_get_all (TpSvcDBusProperties *iface G_GNUC_UNUSED,
+stub_get_all (TpSvcDBusProperties *iface,
     const gchar *i G_GNUC_UNUSED,
     DBusGMethodInvocation *context)
 {
+  PropertylessConnectionManager *cm = (PropertylessConnectionManager *) iface;
+
+  /* Emulate the CM exiting and coming back. */
+  if (cm->drop_name_on_get)
+    {
+      TpDBusDaemon *dbus = tp_base_connection_manager_get_dbus_daemon (
+          TP_BASE_CONNECTION_MANAGER (cm));
+      GString *string = g_string_new (TP_CM_BUS_NAME_BASE);
+      GError *error = NULL;
+
+      g_string_append (string, "example_echo");
+
+      cm->drop_name_on_get--;
+
+      tp_dbus_daemon_release_name (dbus, string->str, &error);
+      g_assert_no_error (error);
+      tp_dbus_daemon_request_name (dbus, string->str, FALSE, &error);
+      g_assert_no_error (error);
+    }
+
   tp_dbus_g_method_return_not_implemented (context);
 }
 
@@ -980,6 +1005,7 @@ test_dbus_fallback (Test *test,
   gchar *name;
   guint info_source;
   const TestFlags flags = GPOINTER_TO_INT (data);
+  PropertylessConnectionManager *service_cm;
   TpBaseConnectionManager *service_cm_as_base;
   gboolean ok;
 
@@ -987,9 +1013,10 @@ test_dbus_fallback (Test *test,
    * exercise the fallback path */
   g_object_unref (test->service_cm);
   test->service_cm = NULL;
-  test->service_cm = TP_TESTS_ECHO_CONNECTION_MANAGER (tp_tests_object_new_static_class (
-        propertyless_connection_manager_get_type (),
-        NULL));
+  service_cm = tp_tests_object_new_static_class (
+      propertyless_connection_manager_get_type (),
+      NULL);
+  test->service_cm = TP_TESTS_ECHO_CONNECTION_MANAGER (service_cm);
   g_assert (test->service_cm != NULL);
   service_cm_as_base = TP_BASE_CONNECTION_MANAGER (test->service_cm);
   g_assert (service_cm_as_base != NULL);
@@ -1003,6 +1030,15 @@ test_dbus_fallback (Test *test,
   g_assert (TP_IS_CONNECTION_MANAGER (test->cm));
   g_assert_no_error (test->error);
 
+  if (flags & DROP_NAME_ON_GET_TWICE)
+    {
+      service_cm->drop_name_on_get = 2;
+    }
+  else if (flags & DROP_NAME_ON_GET)
+    {
+      service_cm->drop_name_on_get = 1;
+    }
+
   if (flags & ACTIVATE_CM)
     {
       g_test_bug ("23524");
@@ -1029,11 +1065,30 @@ test_dbus_fallback (Test *test,
     }
   else
     {
-      tp_tests_proxy_run_until_prepared (test->cm, NULL);
+      tp_tests_proxy_run_until_prepared_or_failed (test->cm, NULL,
+          &test->error);
     }
 
   g_assert_cmpstr (tp_connection_manager_get_name (test->cm), ==,
       "example_echo");
+
+  if (flags & DROP_NAME_ON_GET_TWICE)
+    {
+      /* If it dies during introspection *twice*, we assume it has crashed
+       * or something. */
+      g_assert_error (test->error, TP_DBUS_ERRORS,
+          TP_DBUS_ERROR_NAME_OWNER_LOST);
+      g_clear_error (&test->error);
+
+      g_assert_cmpuint (tp_proxy_is_prepared (test->cm,
+            TP_CONNECTION_MANAGER_FEATURE_CORE), ==, FALSE);
+      g_assert_cmpuint (tp_connection_manager_is_ready (test->cm), ==, FALSE);
+      g_assert_cmpuint (tp_connection_manager_get_info_source (test->cm), ==,
+          TP_CM_INFO_SOURCE_NONE);
+      return;
+    }
+
+  g_assert_no_error (test->error);
   g_assert_cmpuint (tp_proxy_is_prepared (test->cm,
         TP_CONNECTION_MANAGER_FEATURE_CORE), ==, TRUE);
   g_assert (tp_proxy_get_invalidated (test->cm) == NULL);
@@ -1190,6 +1245,13 @@ main (int argc,
       GINT_TO_POINTER (ACTIVATE_CM | USE_CWR),
       setup, test_dbus_fallback, teardown);
 
+  g_test_add ("/cm/dbus-fallback/dies", Test,
+      GINT_TO_POINTER (DROP_NAME_ON_GET), setup, test_dbus_fallback, teardown);
+
+  g_test_add ("/cm/dbus-fallback/dies-twice", Test,
+      GINT_TO_POINTER (DROP_NAME_ON_GET_TWICE),
+      setup, test_dbus_fallback, teardown);
+
   g_test_add ("/cm/list", Test, GINT_TO_POINTER (0),
       setup, test_list, teardown);
   g_test_add ("/cm/list", Test, GINT_TO_POINTER (USE_OLD_LIST),



More information about the telepathy-commits mailing list