[glib] GMainContext: unref pending sources on destroy



commit 8f6be404cbfbda7e188bd164bb72465eeaba6980
Author: Ryan Lortie <desrt desrt ca>
Date:   Fri Oct 25 01:47:37 2013 -0400

    GMainContext: unref pending sources on destroy
    
    It is possible (but unlikely) that there will be a non-empty list of
    pending dispatches when we remove the last ref from a GMainContext.
    Make sure we drop the refs on the sources appropriately.
    
    Add a (now-working) testcase that demonstrates how to trigger the issue.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=139699

 glib/gmain.c          |    5 +++++
 glib/tests/mainloop.c |   42 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 0 deletions(-)
---
diff --git a/glib/gmain.c b/glib/gmain.c
index 37df32c..eb3d1e1 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -507,6 +507,7 @@ g_main_context_unref (GMainContext *context)
   GSource *source;
   GList *sl_iter;
   GSourceList *list;
+  gint i;
 
   g_return_if_fail (context != NULL);
   g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0); 
@@ -518,6 +519,10 @@ g_main_context_unref (GMainContext *context)
   main_context_list = g_slist_remove (main_context_list, context);
   G_UNLOCK (main_context_list);
 
+  /* Free pending dispatches */
+  for (i = 0; i < context->pending_dispatches->len; i++)
+    g_source_unref_internal (context->pending_dispatches->pdata[i], context, FALSE);
+
   /* g_source_iter_next() assumes the context is locked. */
   LOCK_CONTEXT (context);
   g_source_iter_init (&iter, context, TRUE);
diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c
index 09bed49..124ace5 100644
--- a/glib/tests/mainloop.c
+++ b/glib/tests/mainloop.c
@@ -1050,6 +1050,47 @@ test_remove_invalid (void)
   g_test_assert_expected_messages ();
 }
 
+static gboolean
+trivial_prepare (GSource *source,
+                 gint    *timeout)
+{
+  *timeout = 0;
+  return TRUE;
+}
+
+static gint n_finalized;
+
+static void
+trivial_finalize (GSource *source)
+{
+  n_finalized++;
+}
+
+static void
+test_unref_while_pending (void)
+{
+  static GSourceFuncs funcs = { trivial_prepare, NULL, NULL, trivial_finalize };
+  GMainContext *context;
+  GSource *source;
+
+  context = g_main_context_new ();
+
+  source = g_source_new (&funcs, sizeof (GSource));
+  g_source_attach (source, context);
+  g_source_unref (source);
+
+  /* Do incomplete main iteration -- get a pending source but don't dispatch it. */
+  g_main_context_prepare (context, NULL);
+  g_main_context_query (context, 0, NULL, NULL, 0);
+  g_main_context_check (context, 1000, NULL, 0);
+
+  /* Destroy the context */
+  g_main_context_unref (context);
+
+  /* Make sure we didn't leak the source */
+  g_assert_cmpint (n_finalized, ==, 1);
+}
+
 #ifdef G_OS_UNIX
 
 #include <glib-unix.h>
@@ -1495,6 +1536,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/mainloop/ready-time", test_ready_time);
   g_test_add_func ("/mainloop/wakeup", test_wakeup);
   g_test_add_func ("/mainloop/remove-invalid", test_remove_invalid);
+  g_test_add_func ("/mainloop/unref-while-pending", test_unref_while_pending);
 #ifdef G_OS_UNIX
   g_test_add_func ("/mainloop/unix-fd", test_unix_fd);
   g_test_add_func ("/mainloop/unix-fd-source", test_unix_fd_source);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]