gmain: move source destroy write unlock slightly earlier

If a source is using g_source_set_callback_indirect(), then performing
GSource operations within GSourceCallbackFuncs::unref should not cause a
deadlock.

Fixes https://gitlab.gnome.org/GNOME/glib/-/issues/3725
This commit is contained in:
Matthew Waters
2025-07-09 11:51:29 +10:00
parent 25a176869d
commit fe1c7dfdc6
2 changed files with 35 additions and 2 deletions

View File

@@ -574,6 +574,8 @@ retry_decrement:
} }
g_source_iter_clear (&iter); g_source_iter_clear (&iter);
g_rw_lock_writer_unlock (&source_destroy_lock);
/* Next destroy all sources. As we still hold a reference to all of them, /* Next destroy all sources. As we still hold a reference to all of them,
* this won't cause any of them to be freed yet and especially prevents any * this won't cause any of them to be freed yet and especially prevents any
* source that unrefs another source from its finalize function to be freed. * source that unrefs another source from its finalize function to be freed.
@@ -584,8 +586,6 @@ retry_decrement:
g_source_destroy_internal (source, context, TRUE); g_source_destroy_internal (source, context, TRUE);
} }
g_rw_lock_writer_unlock (&source_destroy_lock);
/* the context is going to die now */ /* the context is going to die now */
g_return_if_fail (old_ref > 0); g_return_if_fail (old_ref > 0);

View File

@@ -2384,6 +2384,38 @@ test_maincontext_source_finalization_from_dispatch (gconstpointer user_data)
g_main_context_unref (c); g_main_context_unref (c);
} }
static void
callback_source_unref (gpointer cb_data)
{
GSource *s = (GSource *) cb_data;
g_source_destroy (s);
};
static GSourceCallbackFuncs callback_funcs = {
NULL,
callback_source_unref,
NULL,
};
static void
test_context_ref_while_in_source_callbackfuncs_unref (void)
{
GMainContext *c = g_main_context_new ();
GSource *s;
g_test_summary ("Tests if calling GSource API in GSourceCallbackFuncs.unref "
"does not deadlock attempting to retrieve the relevant GMainContext.");
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/3725");
s = g_source_new (&source_with_source_funcs, sizeof (SourceWithSource));
g_source_set_callback_indirect (s, s, &callback_funcs);
g_source_attach (s, c);
g_source_unref (s);
g_main_context_unref (c);
}
static void static void
once_cb (gpointer user_data) once_cb (gpointer user_data)
{ {
@@ -2671,6 +2703,7 @@ main (int argc, char *argv[])
} }
g_test_add_func ("/maincontext/idle-once", test_maincontext_idle_once); g_test_add_func ("/maincontext/idle-once", test_maincontext_idle_once);
g_test_add_func ("/maincontext/timeout-once", test_maincontext_timeout_once); g_test_add_func ("/maincontext/timeout-once", test_maincontext_timeout_once);
g_test_add_func ("/maincontext/context-ref-in-source-callbackfuncs-unref", test_context_ref_while_in_source_callbackfuncs_unref);
g_test_add_func ("/mainloop/basic", test_mainloop_basic); g_test_add_func ("/mainloop/basic", test_mainloop_basic);
g_test_add_func ("/mainloop/timeouts", test_timeouts); g_test_add_func ("/mainloop/timeouts", test_timeouts);