From cece6abb0899223a72563c7c8bfbc4f1bcd33eb2 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Mon, 10 Jan 2022 23:34:24 +0200 Subject: service-manager: Replace agent wait timeout with a queue When GeoClue is started with D-Bus activation, the agent is not started yet and the starting client currently has to wait AGENT_WAIT_TIMEOUT (=20 s) before being started. Replace the agent wait timeout with a FIFO queue which is processed as soon as the agent connects. Handle the case where client vanishes before agent is connected. Remove the now unused var priv->init_time. Fixes #141. --- src/gclue-service-manager.c | 74 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/gclue-service-manager.c b/src/gclue-service-manager.c index 69fba51..ba06c35 100644 --- a/src/gclue-service-manager.c +++ b/src/gclue-service-manager.c @@ -31,11 +31,6 @@ #include "gclue-locator.h" #include "gclue-config.h" -/* 20 seconds as milliseconds */ -#define AGENT_WAIT_TIMEOUT 20000 -/* 20 seconds as microseconds */ -#define AGENT_WAIT_TIMEOUT_USEC (20 * G_USEC_PER_SEC) - static void gclue_service_manager_manager_iface_init (GClueDBusManagerIface *iface); static void @@ -46,10 +41,10 @@ struct _GClueServiceManagerPrivate GDBusConnection *connection; GList *clients; GHashTable *agents; + GQueue *clients_waiting_agent; guint last_client_id; guint num_clients; - gint64 init_time; GClueLocator *locator; }; @@ -189,6 +184,9 @@ complete_get_client (OnClientInfoNewReadyData *data) char *path; guint32 user_id; + /* Disconnect on_peer_vanished_before_completion, if it's there */ + g_signal_handlers_disconnect_by_data (info, data); + user_id = gclue_client_info_get_user_id (info); agent_proxy = g_hash_table_lookup (priv->agents, GINT_TO_POINTER (user_id)); @@ -265,6 +263,30 @@ out: return FALSE; } +static void +on_peer_vanished_before_completion (GClueClientInfo *info, + gpointer user_data) +{ + OnClientInfoNewReadyData *data = (OnClientInfoNewReadyData *) user_data; + GClueServiceManager *self = GCLUE_SERVICE_MANAGER (data->manager); + GClueServiceManagerPrivate *priv = self->priv; + const char *bus_name; + + bus_name = gclue_client_info_get_bus_name (info); + g_debug ("Client `%s` vanished before agent appeared", + bus_name); + g_signal_handlers_disconnect_by_data (info, + user_data); + g_dbus_method_invocation_return_error (data->invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_DISCONNECTED, + "%s vanished before completion", + bus_name); + g_queue_remove (priv->clients_waiting_agent, data); + on_client_info_new_ready_data_free (data); + g_object_unref (info); +} + static void on_client_info_new_ready (GObject *source_object, GAsyncResult *res, @@ -276,7 +298,6 @@ on_client_info_new_ready (GObject *source_object, GClueAgent *agent_proxy; GError *error = NULL; guint32 user_id; - gint64 now; info = gclue_client_info_new_finish (res, &error); if (info == NULL) { @@ -285,7 +306,7 @@ on_client_info_new_ready (GObject *source_object, G_DBUS_ERROR_FAILED, error->message); g_error_free (error); - g_slice_free (OnClientInfoNewReadyData, data); + on_client_info_new_ready_data_free (data); return; } @@ -295,16 +316,20 @@ on_client_info_new_ready (GObject *source_object, user_id = gclue_client_info_get_user_id (info); agent_proxy = g_hash_table_lookup (priv->agents, GINT_TO_POINTER (user_id)); - now = g_get_monotonic_time (); - if (agent_proxy == NULL && - now < (priv->init_time + AGENT_WAIT_TIMEOUT_USEC)) { + if (agent_proxy == NULL) { /* Its possible that geoclue was just launched on GetClient * call, in which case agents need some time to register * themselves to us. */ - g_timeout_add (AGENT_WAIT_TIMEOUT, - (GSourceFunc) complete_get_client, - user_data); + g_queue_push_tail (priv->clients_waiting_agent, data); + g_signal_connect + (info, + "peer-vanished", + G_CALLBACK (on_peer_vanished_before_completion), + user_data); + g_debug ("Client `%s` waiting for agent for user ID '%u'", + gclue_client_info_get_bus_name (info), user_id); + return; } @@ -435,6 +460,7 @@ on_agent_proxy_ready (GObject *source_object, guint32 user_id; GClueAgent *agent; GError *error = NULL; + GList *l; agent = gclue_agent_proxy_new_for_bus_finish (res, &error); if (agent == NULL) @@ -451,6 +477,19 @@ on_agent_proxy_ready (GObject *source_object, gclue_dbus_manager_complete_add_agent (data->manager, data->invocation); + l = priv->clients_waiting_agent->head; + while (l != NULL) { + GList *next = l->next; + OnClientInfoNewReadyData *d = + (OnClientInfoNewReadyData *) l->data; + + if (gclue_client_info_get_user_id(d->client_info) == user_id) { + complete_get_client (d); + g_queue_delete_link (priv->clients_waiting_agent, l); + } + l = next; + } + goto out; error_out: @@ -548,6 +587,11 @@ gclue_service_manager_finalize (GObject *object) priv->clients = NULL; } g_clear_pointer (&priv->agents, g_hash_table_unref); + if (priv->clients_waiting_agent != NULL) { + g_queue_free_full (priv->clients_waiting_agent, + on_client_info_new_ready_data_free); + priv->clients_waiting_agent = NULL; + } /* Chain up to the parent class */ G_OBJECT_CLASS (gclue_service_manager_parent_class)->finalize (object); @@ -670,7 +714,7 @@ gclue_service_manager_init (GClueServiceManager *manager) g_direct_equal, NULL, g_object_unref); - manager->priv->init_time = g_get_monotonic_time (); + manager->priv->clients_waiting_agent = g_queue_new (); } static gboolean -- cgit v1.2.1