X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Ftests%2Fmainloop.c;h=8abd262f1ec300de84929a73d5e11da34e119447;hb=9f5afe3966d31ef6f1e880d950206a0325e6c777;hp=530a53c27cd6c697bd5e74394759f778c8b71c8c;hpb=e1517ca82da834dccf8a862bd84cdd17e36e1124;p=platform%2Fupstream%2Fglib.git diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c index 530a53c..8abd262 100644 --- a/glib/tests/mainloop.c +++ b/glib/tests/mainloop.c @@ -118,6 +118,7 @@ test_maincontext_basic (void) id = g_source_attach (source, ctx); g_source_unref (source); g_assert (g_source_remove_by_user_data (data)); + g_assert (!g_source_remove_by_user_data ((gpointer)0x1234)); g_idle_add (cb, data); g_assert (g_idle_remove_by_data (data)); @@ -193,12 +194,16 @@ test_timeouts (void) g_main_loop_run (loop); - /* this is a race condition; under some circumstances we might not get 10 - * 100ms runs in 1050 ms, so consider 9 as "close enough" */ - g_assert_cmpint (a, >=, 9); + /* We may be delayed for an arbitrary amount of time - for example, + * it's possible for all timeouts to fire exactly once. + */ + g_assert_cmpint (a, >, 0); + g_assert_cmpint (a, >=, b); + g_assert_cmpint (b, >=, c); + g_assert_cmpint (a, <=, 10); - g_assert_cmpint (b, ==, 4); - g_assert_cmpint (c, ==, 3); + g_assert_cmpint (b, <=, 4); + g_assert_cmpint (c, <=, 3); g_main_loop_unref (loop); g_main_context_unref (ctx); @@ -348,6 +353,75 @@ test_invoke (void) g_main_context_unref (ctx); } +/* We can't use timeout sources here because on slow or heavily-loaded + * machines, the test program might not get enough cycles to hit the + * timeouts at the expected times. So instead we define a source that + * is based on the number of GMainContext iterations. + */ + +static gint counter; +static gint64 last_counter_update; + +typedef struct { + GSource source; + gint interval; + gint timeout; +} CounterSource; + +static gboolean +counter_source_prepare (GSource *source, + gint *timeout) +{ + CounterSource *csource = (CounterSource *)source; + gint64 now; + + now = g_source_get_time (source); + if (now != last_counter_update) + { + last_counter_update = now; + counter++; + } + + *timeout = 1; + return counter >= csource->timeout; +} + +static gboolean +counter_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + CounterSource *csource = (CounterSource *) source; + gboolean again; + + again = callback (user_data); + + if (again) + csource->timeout = counter + csource->interval; + + return again; +} + +static GSourceFuncs counter_source_funcs = { + counter_source_prepare, + NULL, + counter_source_dispatch, + NULL, +}; + +static GSource * +counter_source_new (gint interval) +{ + GSource *source = g_source_new (&counter_source_funcs, sizeof (CounterSource)); + CounterSource *csource = (CounterSource *) source; + + csource->interval = interval; + csource->timeout = counter + interval; + + return source; +} + + static gboolean run_inner_loop (gpointer user_data) { @@ -358,7 +432,7 @@ run_inner_loop (gpointer user_data) a++; inner = g_main_loop_new (ctx, FALSE); - timeout = g_timeout_source_new (100); + timeout = counter_source_new (100); g_source_set_callback (timeout, quit_loop, inner, NULL); g_source_attach (timeout, ctx); g_source_unref (timeout); @@ -381,16 +455,16 @@ test_child_sources (void) a = b = c = 0; - parent = g_timeout_source_new (2000); + parent = counter_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); + child_b = counter_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); + child_c = counter_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); @@ -404,7 +478,7 @@ test_child_sources (void) 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); + end = counter_source_new (1050); g_source_set_callback (end, quit_loop, loop, NULL); g_source_attach (end, ctx); g_source_unref (end); @@ -459,20 +533,20 @@ test_recursive_child_sources (void) a = b = c = 0; - parent = g_timeout_source_new (500); + parent = counter_source_new (500); g_source_set_callback (parent, count_calls, &a, NULL); - child_b = g_timeout_source_new (220); + child_b = counter_source_new (220); g_source_set_callback (child_b, count_calls, &b, NULL); g_source_add_child_source (parent, child_b); - child_c = g_timeout_source_new (430); + child_c = counter_source_new (430); g_source_set_callback (child_c, count_calls, &c, NULL); g_source_add_child_source (child_b, child_c); g_source_attach (parent, ctx); - end = g_timeout_source_new (2010); + end = counter_source_new (2010); g_source_set_callback (end, (GSourceFunc)g_main_loop_quit, loop, NULL); g_source_attach (end, ctx); g_source_unref (end); @@ -480,15 +554,15 @@ test_recursive_child_sources (void) g_main_loop_run (loop); /* Sequence of events: - * 220 b (b = 440, a = 720) - * 430 c (c = 860, b = 650, a = 930) - * 650 b (b = 870, a = 1150) - * 860 c (c = 1290, b = 1080, a = 1360) - * 1080 b (b = 1300, a = 1580) - * 1290 c (c = 1720, b = 1510, a = 1790) - * 1510 b (b = 1730, a = 2010) - * 1720 c (c = 2150, b = 1940, a = 2220) - * 1940 b (b = 2160, a = 2440) + * 220 b (b -> 440, a -> 720) + * 430 c (c -> 860, b -> 650, a -> 930) + * 650 b (b -> 870, a -> 1150) + * 860 c (c -> 1290, b -> 1080, a -> 1360) + * 1080 b (b -> 1300, a -> 1580) + * 1290 c (c -> 1720, b -> 1510, a -> 1790) + * 1510 b (b -> 1730, a -> 2010) + * 1720 c (c -> 2150, b -> 1940, a -> 2220) + * 1940 b (b -> 2160, a -> 2440) */ g_assert_cmpint (a, ==, 9); @@ -548,12 +622,12 @@ test_swapping_child_sources (void) ctx = g_main_context_new (); loop = g_main_loop_new (ctx, FALSE); - data.parent = g_timeout_source_new (50); + data.parent = counter_source_new (50); data.loop = loop; g_source_set_callback (data.parent, swap_sources, &data, NULL); g_source_attach (data.parent, ctx); - data.old_child = g_timeout_source_new (100); + data.old_child = counter_source_new (100); g_source_add_child_source (data.parent, data.old_child); g_source_set_callback (data.old_child, assert_not_reached_callback, NULL, NULL); @@ -568,12 +642,59 @@ test_swapping_child_sources (void) g_main_context_unref (ctx); } +static gboolean +add_source_callback (gpointer user_data) +{ + GMainLoop *loop = user_data; + GSource *self = g_main_current_source (), *child; + GIOChannel *io; + + /* It doesn't matter whether this is a valid fd or not; it never + * actually gets polled; the test is just checking that + * g_source_add_child_source() doesn't crash. + */ + io = g_io_channel_unix_new (0); + child = g_io_create_watch (io, G_IO_IN); + g_source_add_child_source (self, child); + g_source_unref (child); + g_io_channel_unref (io); + + g_main_loop_quit (loop); + return FALSE; +} + +static void +test_blocked_child_sources (void) +{ + GMainContext *ctx; + GMainLoop *loop; + GSource *source; + + g_test_bug ("701283"); + + ctx = g_main_context_new (); + loop = g_main_loop_new (ctx, FALSE); + + source = g_idle_source_new (); + g_source_set_callback (source, add_source_callback, loop, NULL); + g_source_attach (source, ctx); + + g_main_loop_run (loop); + + g_source_destroy (source); + g_source_unref (source); + + g_main_loop_unref (loop); + g_main_context_unref (ctx); +} + typedef struct { GMainContext *ctx; GMainLoop *loop; GSource *timeout1, *timeout2; gint64 time1; + GTimeVal tv; } TimeTestData; static gboolean @@ -594,6 +715,10 @@ timeout1_callback (gpointer user_data) mtime1 = g_get_monotonic_time (); data->time1 = g_source_get_time (source); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + g_source_get_current_time (source, &data->tv); +G_GNUC_END_IGNORE_DEPRECATIONS + /* g_source_get_time() does not change during a single callback */ g_usleep (1000000); mtime2 = g_get_monotonic_time (); @@ -604,6 +729,8 @@ timeout1_callback (gpointer user_data) } else { + GTimeVal tv; + /* Second iteration */ g_assert (g_source_is_destroyed (data->timeout2)); @@ -614,6 +741,14 @@ timeout1_callback (gpointer user_data) time2 = g_source_get_time (source); g_assert_cmpint (data->time1, <, time2); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + g_source_get_current_time (source, &tv); +G_GNUC_END_IGNORE_DEPRECATIONS + + g_assert (tv.tv_sec > data->tv.tv_sec || + (tv.tv_sec == data->tv.tv_sec && + tv.tv_usec > data->tv.tv_usec)); + g_main_loop_quit (data->loop); } @@ -721,7 +856,6 @@ add_idle_source (GMainContext *ctx, return source; } -/* https://bugzilla.gnome.org/show_bug.cgi?id=687098 */ static void test_mainloop_overflow (void) { @@ -731,6 +865,8 @@ test_mainloop_overflow (void) TestOverflowData data; guint i; + g_test_bug ("687098"); + memset (&data, 0, sizeof (data)); ctx = GLIB_PRIVATE_CALL (g_main_context_new_with_next_id) (G_MAXUINT-1); @@ -878,6 +1014,83 @@ test_ready_time (void) g_source_destroy (source); } +static void +test_wakeup(void) +{ + GMainContext *ctx; + int i; + + ctx = g_main_context_new (); + + /* run a random large enough number of times because + * main contexts tend to wake up a few times after creation. + */ + for (i = 0; i < 100; i++) + { + /* This is the invariant we care about: + * g_main_context_wakeup(ctx,) ensures that the next call to + * g_main_context_iteration (ctx, TRUE) returns and doesn't + * block. + * This is important in threaded apps where we might not know + * if the thread calls g_main_context_wakeup() before or after + * we enter g_main_context_iteration(). + */ + g_main_context_wakeup (ctx); + g_main_context_iteration (ctx, TRUE); + } + + g_main_context_unref (ctx); +} + +static void +test_remove_invalid (void) +{ + g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "Source ID 3000000000 was not found*"); + g_source_remove (3000000000u); + 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 @@ -1000,6 +1213,7 @@ assert_main_context_state (gint n_to_poll, GMainContext *context; gboolean consumed[10] = { }; GPollFD poll_fds[10]; + gboolean acquired; gboolean immediate; gint max_priority; gint timeout; @@ -1009,6 +1223,9 @@ assert_main_context_state (gint n_to_poll, context = g_main_context_default (); + acquired = g_main_context_acquire (context); + g_assert (acquired); + immediate = g_main_context_prepare (context, &max_priority); g_assert (!immediate); n = g_main_context_query (context, max_priority, &timeout, poll_fds, 10); @@ -1041,6 +1258,8 @@ assert_main_context_state (gint n_to_poll, if (g_main_context_check (context, max_priority, poll_fds, n)) g_main_context_dispatch (context); + + g_main_context_release (context); } static gboolean @@ -1117,7 +1336,9 @@ test_unix_fd_source (void) g_assert (in && out); g_source_destroy (out_source); + g_source_unref (out_source); g_source_destroy (in_source); + g_source_unref (in_source); close (fds[1]); close (fds[0]); } @@ -1247,12 +1468,107 @@ test_source_unix_fd_api (void) close (fds_b[1]); } +static gboolean +unixfd_quit_loop (gint fd, + GIOCondition condition, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + + return FALSE; +} + +static void +test_unix_file_poll (void) +{ + gint fd; + GSource *source; + GMainLoop *loop; + + fd = open ("/dev/null", O_RDONLY); + g_assert (fd >= 0); + + loop = g_main_loop_new (NULL, FALSE); + + source = g_unix_fd_source_new (fd, G_IO_IN); + g_source_set_callback (source, (GSourceFunc) unixfd_quit_loop, loop, NULL); + g_source_attach (source, NULL); + + /* Should not block */ + g_main_loop_run (loop); + + g_source_destroy (source); + + assert_main_context_state (0); + + g_source_unref (source); + + g_main_loop_unref (loop); + + close (fd); +} + #endif +static gboolean +timeout_cb (gpointer data) +{ + GMainLoop *loop = data; + GMainContext *context; + + context = g_main_loop_get_context (loop); + g_assert (g_main_loop_is_running (loop)); + g_assert (g_main_context_is_owner (context)); + + g_main_loop_quit (loop); + + return G_SOURCE_REMOVE; +} + +static gpointer +threadf (gpointer data) +{ + GMainContext *context = data; + GMainLoop *loop; + GSource *source; + + loop = g_main_loop_new (context, FALSE); + source = g_timeout_source_new (250); + g_source_set_callback (source, timeout_cb, loop, NULL); + g_source_attach (source, context); + g_source_unref (source); + + g_main_loop_run (loop); + + g_main_loop_unref (loop); + + return NULL; +} + +static void +test_mainloop_wait (void) +{ + GMainContext *context; + GThread *t1, *t2; + + context = g_main_context_new (); + + t1 = g_thread_new ("t1", threadf, context); + t2 = g_thread_new ("t2", threadf, context); + + g_thread_join (t1); + g_thread_join (t2); + + g_main_context_unref (context); +} + int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/"); g_test_add_func ("/maincontext/basic", test_maincontext_basic); g_test_add_func ("/mainloop/basic", test_mainloop_basic); @@ -1262,13 +1578,19 @@ main (int argc, char *argv[]) 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/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); g_test_add_func ("/mainloop/overflow", test_mainloop_overflow); 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); g_test_add_func ("/mainloop/source-unix-fd-api", test_source_unix_fd_api); + g_test_add_func ("/mainloop/wait", test_mainloop_wait); + g_test_add_func ("/mainloop/unix-file-poll", test_unix_file_poll); #endif return g_test_run ();