diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c index 4d279852a..8c2f2c5a3 100644 --- a/glib/tests/mainloop.c +++ b/glib/tests/mainloop.c @@ -312,6 +312,103 @@ test_invoke (void) g_assert_cmpint (count, ==, 3); } +static gboolean +run_inner_loop (gpointer user_data) +{ + GMainContext *ctx = user_data; + GMainLoop *inner; + GSource *timeout; + + a++; + + inner = g_main_loop_new (ctx, FALSE); + timeout = g_timeout_source_new (100); + g_source_set_callback (timeout, (GSourceFunc)g_main_loop_quit, inner, NULL); + g_source_attach (timeout, ctx); + + g_main_loop_run (inner); + g_main_loop_unref (inner); + + return TRUE; +} + +static void +test_child_sources (void) +{ + GMainContext *ctx; + GMainLoop *loop; + GSource *parent, *child_b, *child_c, *end; + + ctx = g_main_context_new (); + loop = g_main_loop_new (ctx, FALSE); + + a = b = c = 0; + + parent = g_timeout_source_new (2000); + g_source_set_callback (parent, run_inner_loop, ctx, NULL); + g_source_set_priority (parent, G_PRIORITY_LOW); + g_source_attach (parent, ctx); + + child_b = g_timeout_source_new (250); + g_source_set_callback (child_b, count_calls, &b, NULL); + g_source_add_child_source (parent, child_b); + + child_c = g_timeout_source_new (330); + g_source_set_callback (child_c, count_calls, &c, NULL); + g_source_set_priority (child_c, G_PRIORITY_HIGH); + g_source_add_child_source (parent, child_c); + + /* Child sources always have the priority of the parent */ + g_assert_cmpint (g_source_get_priority (parent), ==, G_PRIORITY_LOW); + g_assert_cmpint (g_source_get_priority (child_b), ==, G_PRIORITY_LOW); + g_assert_cmpint (g_source_get_priority (child_c), ==, G_PRIORITY_LOW); + g_source_set_priority (parent, G_PRIORITY_DEFAULT); + g_assert_cmpint (g_source_get_priority (parent), ==, G_PRIORITY_DEFAULT); + g_assert_cmpint (g_source_get_priority (child_b), ==, G_PRIORITY_DEFAULT); + g_assert_cmpint (g_source_get_priority (child_c), ==, G_PRIORITY_DEFAULT); + + end = g_timeout_source_new (1050); + g_source_set_callback (end, (GSourceFunc)g_main_loop_quit, loop, NULL); + g_source_attach (end, ctx); + g_source_unref (end); + + g_main_loop_run (loop); + + /* The parent source's own timeout will never trigger, so "a" will + * only get incremented when "b" or "c" does. And when timeouts get + * blocked, they still wait the full interval next time rather than + * "catching up". So the timing is: + * + * 250 - b++ -> a++, run_inner_loop + * 330 - (c is blocked) + * 350 - inner_loop ends + * 350 - c++ belatedly -> a++, run_inner_loop + * 450 - inner loop ends + * 500 - b++ -> a++, run_inner_loop + * 600 - inner_loop ends + * 680 - c++ -> a++, run_inner_loop + * 750 - (b is blocked) + * 780 - inner loop ends + * 780 - b++ belatedly -> a++, run_inner_loop + * 880 - inner loop ends + * 1010 - c++ -> a++, run_inner_loop + * 1030 - (b is blocked) + * 1050 - end runs, quits outer loop, which has no effect yet + * 1110 - inner loop ends, a returns, outer loop exits + */ + + g_assert_cmpint (a, ==, 6); + g_assert_cmpint (b, ==, 3); + g_assert_cmpint (c, ==, 3); + + g_source_unref (parent); + g_source_unref (child_b); + g_source_unref (child_c); + + g_main_loop_unref (loop); + g_main_context_unref (ctx); +} + int main (int argc, char *argv[]) { @@ -322,6 +419,7 @@ main (int argc, char *argv[]) g_test_add_func ("/mainloop/timeouts", test_timeouts); g_test_add_func ("/mainloop/priorities", test_priorities); g_test_add_func ("/mainloop/invoke", test_invoke); + g_test_add_func ("/mainloop/child_sources", test_child_sources); return g_test_run (); }