summaryrefslogtreecommitdiff
path: root/glib
diff options
context:
space:
mode:
authorMarco Trevisan (Treviño) <mail@3v1n0.net>2023-04-19 22:43:42 +0200
committerMarco Trevisan (Treviño) <mail@3v1n0.net>2023-04-19 22:52:43 +0200
commit7d5242d34ecbe0dd07e8ae5fe8a1cc61316dae26 (patch)
tree4901dc1b4cab99a8f32091e940e77de44da5760b /glib
parentf8e440335ce2eb4e9d5474b4c50b06b184611baa (diff)
downloadglib-7d5242d34ecbe0dd07e8ae5fe8a1cc61316dae26.tar.gz
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.
Diffstat (limited to 'glib')
-rw-r--r--glib/gmain.c4
-rw-r--r--glib/tests/mainloop.c70
2 files changed, 73 insertions, 1 deletions
diff --git a/glib/gmain.c b/glib/gmain.c
index 69de75184..7442d3d05 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -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;
}
diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c
index 9904da833..b934dc8c7 100644
--- a/glib/tests/mainloop.c
+++ b/glib/tests/mainloop.c
@@ -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);