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