telepathy-glib: Retry to prepare features which failed because of a missing conn iface

Guillaume Desmottes gdesmott at kemper.freedesktop.org
Fri Apr 27 01:44:13 PDT 2012


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

Author: Guillaume Desmottes <guillaume.desmottes at collabora.co.uk>
Date:   Wed Nov 16 14:23:21 2011 +0100

Retry to prepare features which failed because of a missing conn iface

If we try to prepare a connection feature before the connection is connected,
the preparation may fall if a required interface hasn't been announced yet by
the CM. So, we give those features a second chance to be prepared when the
connection has been connected.

https://bugs.freedesktop.org/show_bug.cgi?id=42981

---

 telepathy-glib/proxy.c         |   56 ++++++++++++++++++++++++++++++++--------
 tests/dbus/proxy-preparation.c |   39 +++++++++++++++++++++++++++
 tests/lib/my-conn-proxy.c      |   30 +++++++++++++++++++++
 tests/lib/my-conn-proxy.h      |    9 ++++++
 4 files changed, 123 insertions(+), 11 deletions(-)

diff --git a/telepathy-glib/proxy.c b/telepathy-glib/proxy.c
index 4e8e839..6533877 100644
--- a/telepathy-glib/proxy.c
+++ b/telepathy-glib/proxy.c
@@ -323,6 +323,11 @@ typedef enum {
     FEATURE_STATE_WANTED,
     /* Want to prepare, have called prepare_async */
     FEATURE_STATE_TRYING,
+    /* Couldn't prepare because a required interface on the connection
+     * was missing and the connection wasn't connected yet. We'll retry to
+     * prepare once the connection is connected.
+     * This state is only used when preparing Connection features */
+    FEATURE_STATE_MISSING_IFACE,
     /* Couldn't prepare, gave up */
     FEATURE_STATE_FAILED,
     /* Prepared */
@@ -1789,6 +1794,7 @@ check_depends_ready (TpProxy *self,
             return FALSE;
 
           case FEATURE_STATE_FAILED:
+          case FEATURE_STATE_MISSING_IFACE:
             if (!can_retry || !dep_feature->can_retry)
               {
                 DEBUG ("Can't prepare %s, because %s (a dependency) is "
@@ -2089,8 +2095,21 @@ request_is_complete (TpProxy *self,
                  * in tp_proxy_prepare_async() as CORE have to be prepared */
                 if (!check_feature_interfaces (self, feature))
                   {
-                    tp_proxy_set_feature_state (self, feature,
-                        FEATURE_STATE_FAILED);
+                    if (TP_IS_CONNECTION (self) &&
+                        tp_connection_get_status ((TpConnection *) self, NULL)
+                        != TP_CONNECTION_STATUS_CONNECTED)
+                      {
+                        /* Give a chance to retry preparing the feature once
+                         * the Connection is connected as it may still gain
+                         * the interface. */
+                        tp_proxy_set_feature_state (self, feature,
+                            FEATURE_STATE_MISSING_IFACE);
+                      }
+                    else
+                      {
+                        tp_proxy_set_feature_state (self, feature,
+                            FEATURE_STATE_FAILED);
+                      }
                     continue;
                   }
 
@@ -2126,6 +2145,7 @@ request_is_complete (TpProxy *self,
 
           case FEATURE_STATE_INVALID:
           case FEATURE_STATE_FAILED:
+          case FEATURE_STATE_MISSING_IFACE:
           case FEATURE_STATE_READY:
             /* nothing more to do */
             break;
@@ -2318,20 +2338,34 @@ static void foreach_feature (GQuark name,
 {
   FeatureState state = GPOINTER_TO_INT (data);
   TpProxy *self = user_data;
-  const TpProxyFeature *feature;
 
-  if (state != FEATURE_STATE_READY)
-    return;
+  if (state == FEATURE_STATE_MISSING_IFACE)
+    {
+      GQuark features[] = { 0, 0};
 
-  feature = tp_proxy_subclass_get_feature (G_OBJECT_TYPE (self), name);
+      tp_proxy_set_feature_state (self, name, FEATURE_STATE_UNWANTED);
 
-  if (feature->prepare_before_signalling_connected_async == NULL)
-    return;
+      self->priv->pending_will_announce_calls++;
 
-  self->priv->pending_will_announce_calls++;
+      features[0] = name;
 
-  feature->prepare_before_signalling_connected_async (self, feature,
-      prepare_before_signalling_connected_cb, self);
+      tp_proxy_prepare_async (self, features,
+          prepare_before_signalling_connected_cb, self);
+    }
+  else if (state == FEATURE_STATE_READY)
+    {
+      const TpProxyFeature *feature;
+
+      feature = tp_proxy_subclass_get_feature (G_OBJECT_TYPE (self), name);
+
+      if (feature->prepare_before_signalling_connected_async == NULL)
+        return;
+
+      self->priv->pending_will_announce_calls++;
+
+      feature->prepare_before_signalling_connected_async (self, feature,
+          prepare_before_signalling_connected_cb, self);
+    }
 }
 
 /*
diff --git a/tests/dbus/proxy-preparation.c b/tests/dbus/proxy-preparation.c
index df4b1b7..7d7638f 100644
--- a/tests/dbus/proxy-preparation.c
+++ b/tests/dbus/proxy-preparation.c
@@ -368,6 +368,43 @@ test_before_connected (Test *test,
       BEFORE_CONNECTED_STATE_CONNECTED);
 }
 
+static void
+test_interface_later (Test *test,
+    gconstpointer data G_GNUC_UNUSED)
+{
+  GQuark features[] = { TP_TESTS_MY_CONN_PROXY_FEATURE_INTERFACE_LATER, 0 };
+  GQuark connected[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 };
+  const gchar *interfaces[] = { TP_TESTS_MY_CONN_PROXY_IFACE_LATER, NULL };
+
+  /* We need a not yet connected connection */
+  recreate_connection (test);
+
+  /* Try preparing before the connection is connected */
+  tp_proxy_prepare_async (test->my_conn, features, prepare_cb, test);
+
+  g_main_loop_run (test->mainloop);
+  g_assert_no_error (test->error);
+
+  /* Feature isn't prepared */
+  g_assert (!tp_proxy_is_prepared (test->my_conn,
+        TP_TESTS_MY_CONN_PROXY_FEATURE_INTERFACE_LATER));
+
+  tp_cli_connection_call_connect (test->connection, -1, NULL, NULL, NULL, NULL);
+
+  /* While connecting the interface is added */
+  tp_base_connection_add_interfaces (test->base_connection, interfaces);
+
+  /* Wait that CONNECTED is announced */
+  tp_proxy_prepare_async (test->my_conn, connected, prepare_cb, test);
+
+  g_main_loop_run (test->mainloop);
+  g_assert_no_error (test->error);
+
+  /* The feature has been prepared now */
+  g_assert (tp_proxy_is_prepared (test->my_conn,
+        TP_TESTS_MY_CONN_PROXY_FEATURE_INTERFACE_LATER));
+}
+
 int
 main (int argc,
       char **argv)
@@ -395,6 +432,8 @@ main (int argc,
       test_retry_dep, teardown);
   g_test_add ("/proxy-preparation/before-connected", Test, NULL, setup,
       test_before_connected, teardown);
+  g_test_add ("/proxy-preparation/interface-later", Test, NULL, setup,
+      test_interface_later, teardown);
 
   return g_test_run ();
 }
diff --git a/tests/lib/my-conn-proxy.c b/tests/lib/my-conn-proxy.c
index 208f004..d7748f3 100644
--- a/tests/lib/my-conn-proxy.c
+++ b/tests/lib/my-conn-proxy.c
@@ -33,6 +33,7 @@ enum {
     FEAT_RETRY,
     FEAT_RETRY_DEP,
     FEAT_BEFORE_CONNECTED,
+    FEAT_INTERFACE_LATER,
     N_FEAT
 };
 
@@ -203,6 +204,21 @@ prepare_before_connected_before_async (TpProxy *proxy,
   g_object_unref (result);
 }
 
+static void
+prepare_interface_later_async (TpProxy *proxy,
+    const TpProxyFeature *feature,
+    GAsyncReadyCallback callback,
+    gpointer user_data)
+{
+  GSimpleAsyncResult *result;
+
+  result = g_simple_async_result_new ((GObject *) proxy, callback, user_data,
+      prepare_interface_later_async);
+
+  g_simple_async_result_complete_in_idle (result);
+  g_object_unref (result);
+}
+
 static const TpProxyFeature *
 list_features (TpProxyClass *cls G_GNUC_UNUSED)
 {
@@ -212,6 +228,7 @@ list_features (TpProxyClass *cls G_GNUC_UNUSED)
   static GQuark need_wrong_iface[2] = {0, 0};
   static GQuark need_fail[2] = {0, 0};
   static GQuark need_retry[2] = {0, 0};
+  static GQuark need_iface_later[2] = {0, 0};
 
   if (G_LIKELY (features[0].name != 0))
     return features;
@@ -262,6 +279,13 @@ list_features (TpProxyClass *cls G_GNUC_UNUSED)
   features[FEAT_BEFORE_CONNECTED].prepare_before_signalling_connected_async =
     prepare_before_connected_before_async;
 
+  features[FEAT_INTERFACE_LATER].name =
+    TP_TESTS_MY_CONN_PROXY_FEATURE_INTERFACE_LATER;
+  features[FEAT_INTERFACE_LATER].prepare_async = prepare_interface_later_async;
+  need_iface_later[0] = g_quark_from_static_string (
+      TP_TESTS_MY_CONN_PROXY_IFACE_LATER);
+  features[FEAT_INTERFACE_LATER].interfaces_needed = need_iface_later;
+
   return features;
 }
 
@@ -332,3 +356,9 @@ tp_tests_my_conn_proxy_get_feature_quark_before_connected (void)
 {
   return g_quark_from_static_string ("tp-my-conn-proxy-feature-before-connected");
 }
+
+GQuark
+tp_tests_my_conn_proxy_get_feature_quark_interface_later (void)
+{
+  return g_quark_from_static_string ("tp-my-conn-proxy-feature-interface-later");
+}
diff --git a/tests/lib/my-conn-proxy.h b/tests/lib/my-conn-proxy.h
index cfc8216..08be18b 100644
--- a/tests/lib/my-conn-proxy.h
+++ b/tests/lib/my-conn-proxy.h
@@ -110,6 +110,15 @@ GQuark tp_tests_my_conn_proxy_get_feature_quark_retry_dep (void) G_GNUC_CONST;
   (tp_tests_my_conn_proxy_get_feature_quark_before_connected ())
 GQuark tp_tests_my_conn_proxy_get_feature_quark_before_connected (void) G_GNUC_CONST;
 
+#define TP_TESTS_MY_CONN_PROXY_IFACE_LATER "org.freedesktop.Telepathy.Conncetion.Interface.Test.Later"
+
+/* Need the interface TP_TESTS_MY_CONN_PROXY_IFACE_LATER to be prepared but
+ * this interface is not in the initial set of interfaces of the connection.
+ * It is added when the connection is connected. */
+#define TP_TESTS_MY_CONN_PROXY_FEATURE_INTERFACE_LATER \
+  (tp_tests_my_conn_proxy_get_feature_quark_interface_later ())
+GQuark tp_tests_my_conn_proxy_get_feature_quark_interface_later (void) G_GNUC_CONST;
+
 G_END_DECLS
 
 #endif /* #ifndef __TP_TESTS_MY_CONN_PROXY_H__ */



More information about the telepathy-commits mailing list