gmain: Do not dead-look if calling g_main_loop_run from a GSource

This should fail and warn but not leaving the context acquired and
locked.

Add tests.
This commit is contained in:
Marco Trevisan (Treviño) 2023-04-19 22:43:42 +02:00
parent f8e440335c
commit 7d5242d34e
2 changed files with 73 additions and 1 deletions

View File

@ -4522,10 +4522,12 @@ g_main_loop_run (GMainLoop *loop)
g_assert (got_ownership);
}
if (loop->context->in_check_or_prepare)
if G_UNLIKELY (loop->context->in_check_or_prepare)
{
g_warning ("g_main_loop_run(): called recursively from within a source's "
"check() or prepare() member, iteration not possible.");
g_main_context_release_unlocked (loop->context);
UNLOCK_CONTEXT (loop->context);
g_main_loop_unref (loop);
return;
}

View File

@ -1169,6 +1169,75 @@ test_unref_while_pending (void)
g_assert_cmpint (n_finalized, ==, 1);
}
typedef struct {
GSource parent;
GMainLoop *loop;
} LoopedSource;
static gboolean
prepare_loop_run (GSource *source, gint *time)
{
LoopedSource *looped_source = (LoopedSource*) source;
*time = 0;
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
"*called recursively from within a source's check() "
"or prepare() member*");
g_main_loop_run (looped_source->loop);
g_test_assert_expected_messages ();
return FALSE;
}
static gboolean
check_loop_run (GSource *source)
{
LoopedSource *looped_source = (LoopedSource*) source;
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
"*called recursively from within a source's check() "
"or prepare() member*");
g_main_loop_run (looped_source->loop);
g_test_assert_expected_messages ();
return TRUE;
}
static gboolean
dispatch_loop_run (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
LoopedSource *looped_source = (LoopedSource*) source;
g_main_loop_quit (looped_source->loop);
return FALSE;
}
static void
test_recursive_loop_child_sources (void)
{
GMainLoop *loop;
GSource *source;
GSourceFuncs loop_run_funcs = {
prepare_loop_run, check_loop_run, dispatch_loop_run, NULL, NULL, NULL,
};
loop = g_main_loop_new (NULL, FALSE);
source = g_source_new (&loop_run_funcs, sizeof (LoopedSource));
((LoopedSource*)source)->loop = loop;
g_source_attach (source, NULL);
g_main_loop_run (loop);
g_source_unref (source);
g_main_loop_unref (loop);
}
#ifdef G_OS_UNIX
#include <glib-unix.h>
@ -2445,6 +2514,7 @@ main (int argc, char *argv[])
g_test_add_func ("/mainloop/invoke", test_invoke);
g_test_add_func ("/mainloop/child_sources", test_child_sources);
g_test_add_func ("/mainloop/recursive_child_sources", test_recursive_child_sources);
g_test_add_func ("/mainloop/recursive_loop_child_sources", test_recursive_loop_child_sources);
g_test_add_func ("/mainloop/swapping_child_sources", test_swapping_child_sources);
g_test_add_func ("/mainloop/blocked_child_sources", test_blocked_child_sources);
g_test_add_func ("/mainloop/source_time", test_source_time);