From 8f6be404cbfbda7e188bd164bb72465eeaba6980 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Fri, 25 Oct 2013 01:47:37 -0400 Subject: [PATCH] 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(+) 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 @@ -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); -- 2.7.4