1b5975d6bSopenharmony_ciFrom 56d371942e43c52bc6131067e2dc2a35f6cd5a3d Mon Sep 17 00:00:00 2001 2b5975d6bSopenharmony_ciFrom: Philip Withnall <pwithnall@endlessos.org> 3b5975d6bSopenharmony_ciDate: Mon, 13 Jun 2022 13:06:06 +0100 4b5975d6bSopenharmony_ciSubject: [PATCH] gsocketclient: Fix still-reachable references to cancellables 5b5975d6bSopenharmony_ciMIME-Version: 1.0 6b5975d6bSopenharmony_ciContent-Type: text/plain; charset=UTF-8 7b5975d6bSopenharmony_ciContent-Transfer-Encoding: 8bit 8b5975d6bSopenharmony_ci 9b5975d6bSopenharmony_ci`GSocketClient` chains its internal `GCancellable` objects to ones 10b5975d6bSopenharmony_ciprovided by the caller in two places using `g_cancellable_connect()`. 11b5975d6bSopenharmony_ciHowever, it never calls `g_cancellable_disconnect()`, instead relying 12b5975d6bSopenharmony_ci(incorrectly) on the `GCancellable` provided by the caller being 13b5975d6bSopenharmony_cishort-lived. 14b5975d6bSopenharmony_ci 15b5975d6bSopenharmony_ciIn the (valid) situation where a caller reuses one `GCancellable` for 16b5975d6bSopenharmony_cimultiple socket client calls, or for calls across multiple socket 17b5975d6bSopenharmony_ciclients, this will cause the internal `GCancellable` objects from those 18b5975d6bSopenharmony_ci`GSocketClient`s to accumulate, with one reference left each (which is 19b5975d6bSopenharmony_cithe reference from the `g_cancellable_connect()` closure). 20b5975d6bSopenharmony_ci 21b5975d6bSopenharmony_ciThese `GCancellable` instances aren't technically leaked, as they will 22b5975d6bSopenharmony_ciall be freed when the caller's `GCancellable` is disposed, but they are 23b5975d6bSopenharmony_cino longer useful and there is no bound on the number of them which will 24b5975d6bSopenharmony_cihang around. 25b5975d6bSopenharmony_ci 26b5975d6bSopenharmony_ciFor a program doing a lot of socket operations, this still-reachable 27b5975d6bSopenharmony_cimemory usage can become significant. 28b5975d6bSopenharmony_ci 29b5975d6bSopenharmony_ciFix the problem by adding paired `g_cancellable_disconnect()` calls. 30b5975d6bSopenharmony_ciIt's not possible to add a unit test as we can't measure still-reachable 31b5975d6bSopenharmony_cimemory growth before the end of a unit test when everything has to be 32b5975d6bSopenharmony_cifreed. 33b5975d6bSopenharmony_ci 34b5975d6bSopenharmony_ciSigned-off-by: Philip Withnall <pwithnall@endlessos.org> 35b5975d6bSopenharmony_ci 36b5975d6bSopenharmony_ciFixes: #2670 37b5975d6bSopenharmony_ci 38b5975d6bSopenharmony_ciConflict:NA 39b5975d6bSopenharmony_ciReference:https://gitlab.gnome.org/GNOME/glib/-/commit/56d371942e43c52bc6131067e2dc2a35f6cd5a3d 40b5975d6bSopenharmony_ci 41b5975d6bSopenharmony_ci--- 42b5975d6bSopenharmony_ci gio/gsocketclient.c | 23 +++++++++++++++++++---- 43b5975d6bSopenharmony_ci 1 file changed, 19 insertions(+), 4 deletions(-) 44b5975d6bSopenharmony_ci 45b5975d6bSopenharmony_cidiff --git a/gio/gsocketclient.c b/gio/gsocketclient.c 46b5975d6bSopenharmony_ciindex ae80f5203c..127915b722 100644 47b5975d6bSopenharmony_ci--- a/gio/gsocketclient.c 48b5975d6bSopenharmony_ci+++ b/gio/gsocketclient.c 49b5975d6bSopenharmony_ci@@ -1466,6 +1466,8 @@ typedef struct 50b5975d6bSopenharmony_ci GSocketConnectable *connectable; 51b5975d6bSopenharmony_ci GSocketAddressEnumerator *enumerator; 52b5975d6bSopenharmony_ci GCancellable *enumeration_cancellable; 53b5975d6bSopenharmony_ci+ GCancellable *enumeration_parent_cancellable; /* (nullable) (owned) */ 54b5975d6bSopenharmony_ci+ gulong enumeration_cancelled_id; 55b5975d6bSopenharmony_ci 56b5975d6bSopenharmony_ci GSList *connection_attempts; 57b5975d6bSopenharmony_ci GSList *successful_connections; 58b5975d6bSopenharmony_ci@@ -1485,7 +1487,12 @@ g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data) 59b5975d6bSopenharmony_ci data->task = NULL; 60b5975d6bSopenharmony_ci g_clear_object (&data->connectable); 61b5975d6bSopenharmony_ci g_clear_object (&data->enumerator); 62b5975d6bSopenharmony_ci+ 63b5975d6bSopenharmony_ci+ g_cancellable_disconnect (data->enumeration_parent_cancellable, data->enumeration_cancelled_id); 64b5975d6bSopenharmony_ci+ g_clear_object (&data->enumeration_parent_cancellable); 65b5975d6bSopenharmony_ci+ data->enumeration_cancelled_id = 0; 66b5975d6bSopenharmony_ci g_clear_object (&data->enumeration_cancellable); 67b5975d6bSopenharmony_ci+ 68b5975d6bSopenharmony_ci g_slist_free_full (data->connection_attempts, connection_attempt_unref); 69b5975d6bSopenharmony_ci g_slist_free_full (data->successful_connections, connection_attempt_unref); 70b5975d6bSopenharmony_ci 71b5975d6bSopenharmony_ci@@ -1503,6 +1510,7 @@ typedef struct 72b5975d6bSopenharmony_ci GSocketClientAsyncConnectData *data; /* unowned */ 73b5975d6bSopenharmony_ci GSource *timeout_source; 74b5975d6bSopenharmony_ci GCancellable *cancellable; 75b5975d6bSopenharmony_ci+ gulong cancelled_id; 76b5975d6bSopenharmony_ci grefcount ref; 77b5975d6bSopenharmony_ci } ConnectionAttempt; 78b5975d6bSopenharmony_ci 79b5975d6bSopenharmony_ci@@ -1530,6 +1538,8 @@ connection_attempt_unref (gpointer pointer) 80b5975d6bSopenharmony_ci g_clear_object (&attempt->address); 81b5975d6bSopenharmony_ci g_clear_object (&attempt->socket); 82b5975d6bSopenharmony_ci g_clear_object (&attempt->connection); 83b5975d6bSopenharmony_ci+ g_cancellable_disconnect (g_task_get_cancellable (attempt->data->task), attempt->cancelled_id); 84b5975d6bSopenharmony_ci+ attempt->cancelled_id = 0; 85b5975d6bSopenharmony_ci g_clear_object (&attempt->cancellable); 86b5975d6bSopenharmony_ci g_clear_object (&attempt->proxy_addr); 87b5975d6bSopenharmony_ci if (attempt->timeout_source) 88b5975d6bSopenharmony_ci@@ -2023,8 +2033,9 @@ g_socket_client_enumerator_callback (GObject *object, 89b5975d6bSopenharmony_ci data->connection_attempts = g_slist_append (data->connection_attempts, attempt); 90b5975d6bSopenharmony_ci 91b5975d6bSopenharmony_ci if (g_task_get_cancellable (data->task)) 92b5975d6bSopenharmony_ci- g_cancellable_connect (g_task_get_cancellable (data->task), G_CALLBACK (on_connection_cancelled), 93b5975d6bSopenharmony_ci- g_object_ref (attempt->cancellable), g_object_unref); 94b5975d6bSopenharmony_ci+ attempt->cancelled_id = 95b5975d6bSopenharmony_ci+ g_cancellable_connect (g_task_get_cancellable (data->task), G_CALLBACK (on_connection_cancelled), 96b5975d6bSopenharmony_ci+ g_object_ref (attempt->cancellable), g_object_unref); 97b5975d6bSopenharmony_ci 98b5975d6bSopenharmony_ci g_socket_connection_set_cached_remote_address ((GSocketConnection *)attempt->connection, address); 99b5975d6bSopenharmony_ci g_debug ("GSocketClient: Starting TCP connection attempt"); 100b5975d6bSopenharmony_ci@@ -2129,8 +2140,12 @@ g_socket_client_connect_async (GSocketClient *client, 101b5975d6bSopenharmony_ci 102b5975d6bSopenharmony_ci data->enumeration_cancellable = g_cancellable_new (); 103b5975d6bSopenharmony_ci if (cancellable) 104b5975d6bSopenharmony_ci- g_cancellable_connect (cancellable, G_CALLBACK (on_connection_cancelled), 105b5975d6bSopenharmony_ci- g_object_ref (data->enumeration_cancellable), g_object_unref); 106b5975d6bSopenharmony_ci+ { 107b5975d6bSopenharmony_ci+ data->enumeration_parent_cancellable = g_object_ref (cancellable); 108b5975d6bSopenharmony_ci+ data->enumeration_cancelled_id = 109b5975d6bSopenharmony_ci+ g_cancellable_connect (cancellable, G_CALLBACK (on_connection_cancelled), 110b5975d6bSopenharmony_ci+ g_object_ref (data->enumeration_cancellable), g_object_unref); 111b5975d6bSopenharmony_ci+ } 112b5975d6bSopenharmony_ci 113b5975d6bSopenharmony_ci enumerator_next_async (data, FALSE); 114b5975d6bSopenharmony_ci } 115b5975d6bSopenharmony_ci-- 116b5975d6bSopenharmony_ciGitLab 117b5975d6bSopenharmony_ci 118