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));
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);
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)
{
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);
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);
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);
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);
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);
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);
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
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 ();
}
else
{
+ GTimeVal tv;
+
/* Second iteration */
g_assert (g_source_is_destroyed (data->timeout2));
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);
}
return source;
}
-/* https://bugzilla.gnome.org/show_bug.cgi?id=687098 */
static void
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);
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 <glib-unix.h>
GMainContext *context;
gboolean consumed[10] = { };
GPollFD poll_fds[10];
+ gboolean acquired;
gboolean immediate;
gint max_priority;
gint timeout;
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);
if (g_main_context_check (context, max_priority, poll_fds, n))
g_main_context_dispatch (context);
+
+ g_main_context_release (context);
}
static gboolean
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]);
}
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);
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 ();