From fa02906c03e7c03096d72754599745e23c75691a Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Wed, 16 Nov 2011 14:23:21 +0100 Subject: [PATCH] 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 https://bugs.freedesktop.org/show_bug.cgi?id=31583 --- 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 6153818..9f8ac07 100644 --- a/telepathy-glib/proxy.c +++ b/telepathy-glib/proxy.c @@ -332,6 +332,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 */ @@ -1798,6 +1803,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 " @@ -2098,8 +2104,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; } @@ -2135,6 +2154,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; @@ -2327,20 +2347,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__ */ -- 1.7.7.6