1/* Unit tests for GMainLoop
2 * Copyright (C) 2011 Red Hat, Inc
3 * Author: Matthias Clasen
4 *
5 * This work is provided "as is"; redistribution and modification
6 * in whole or in part, in any medium, physical or electronic is
7 * permitted without restriction.
8 *
9 * This work is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * In no event shall the authors or contributors be liable for any
14 * direct, indirect, incidental, special, exemplary, or consequential
15 * damages (including, but not limited to, procurement of substitute
16 * goods or services; loss of use, data, or profits; or business
17 * interruption) however caused and on any theory of liability, whether
18 * in contract, strict liability, or tort (including negligence or
19 * otherwise) arising in any way out of the use of this software, even
20 * if advised of the possibility of such damage.
21 */
22
23#include <glib.h>
24#include "glib-private.h"
25#include <stdio.h>
26#include <string.h>
27
28static gboolean
29cb (gpointer data)
30{
31 return FALSE;
32}
33
34static gboolean
35prepare (GSource *source, gint *time)
36{
37 return FALSE;
38}
39static gboolean
40check (GSource *source)
41{
42 return FALSE;
43}
44static gboolean
45dispatch (GSource *source, GSourceFunc cb, gpointer date)
46{
47 return FALSE;
48}
49
50static GSourceFuncs funcs = {
51 prepare,
52 check,
53 dispatch,
54 NULL,
55 NULL,
56 NULL
57};
58
59static void
60test_maincontext_basic (void)
61{
62 GMainContext *ctx;
63 GSource *source;
64 guint id;
65 gpointer data = &funcs;
66
67 ctx = g_main_context_new ();
68
69 g_assert_false (g_main_context_pending (ctx));
70 g_assert_false (g_main_context_iteration (ctx, FALSE));
71
72 source = g_source_new (source_funcs: &funcs, struct_size: sizeof (GSource));
73 g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_DEFAULT);
74 g_assert_false (g_source_is_destroyed (source));
75
76 g_assert_false (g_source_get_can_recurse (source));
77 g_assert_null (g_source_get_name (source));
78
79 g_source_set_can_recurse (source, TRUE);
80 g_source_set_name (source, name: "d");
81
82 g_assert_true (g_source_get_can_recurse (source));
83 g_assert_cmpstr (g_source_get_name (source), ==, "d");
84
85 g_assert_null (g_main_context_find_source_by_user_data (ctx, NULL));
86 g_assert_null (g_main_context_find_source_by_funcs_user_data (ctx, &funcs, NULL));
87
88 id = g_source_attach (source, context: ctx);
89 g_assert_cmpint (g_source_get_id (source), ==, id);
90 g_assert_true (g_main_context_find_source_by_id (ctx, id) == source);
91
92 g_source_set_priority (source, G_PRIORITY_HIGH);
93 g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_HIGH);
94
95 g_source_destroy (source);
96 g_assert_true (g_source_get_context (source) == ctx);
97 g_assert_null (g_main_context_find_source_by_id (ctx, id));
98
99 g_main_context_unref (context: ctx);
100
101 if (g_test_undefined ())
102 {
103 g_test_expect_message (G_LOG_DOMAIN, log_level: G_LOG_LEVEL_CRITICAL,
104 pattern: "*assertion*source->context != NULL*failed*");
105 g_assert_null (g_source_get_context (source));
106 g_test_assert_expected_messages ();
107 }
108
109 g_source_unref (source);
110
111 ctx = g_main_context_default ();
112 source = g_source_new (source_funcs: &funcs, struct_size: sizeof (GSource));
113 g_source_set_funcs (source, funcs: &funcs);
114 g_source_set_callback (source, func: cb, data, NULL);
115 id = g_source_attach (source, context: ctx);
116 g_source_unref (source);
117 g_source_set_name_by_id (tag: id, name: "e");
118 g_assert_cmpstr (g_source_get_name (source), ==, "e");
119 g_assert_true (g_source_get_context (source) == ctx);
120 g_assert_true (g_source_remove_by_funcs_user_data (&funcs, data));
121
122 source = g_source_new (source_funcs: &funcs, struct_size: sizeof (GSource));
123 g_source_set_funcs (source, funcs: &funcs);
124 g_source_set_callback (source, func: cb, data, NULL);
125 id = g_source_attach (source, context: ctx);
126 g_assert_cmpint (id, >, 0);
127 g_source_unref (source);
128 g_assert_true (g_source_remove_by_user_data (data));
129 g_assert_false (g_source_remove_by_user_data ((gpointer)0x1234));
130
131 g_idle_add (function: cb, data);
132 g_assert_true (g_idle_remove_by_data (data));
133}
134
135static void
136test_mainloop_basic (void)
137{
138 GMainLoop *loop;
139 GMainContext *ctx;
140
141 loop = g_main_loop_new (NULL, FALSE);
142
143 g_assert_false (g_main_loop_is_running (loop));
144
145 g_main_loop_ref (loop);
146
147 ctx = g_main_loop_get_context (loop);
148 g_assert_true (ctx == g_main_context_default ());
149
150 g_main_loop_unref (loop);
151
152 g_assert_cmpint (g_main_depth (), ==, 0);
153
154 g_main_loop_unref (loop);
155}
156
157static gint a;
158static gint b;
159static gint c;
160
161static gboolean
162count_calls (gpointer data)
163{
164 gint *i = data;
165
166 (*i)++;
167
168 return TRUE;
169}
170
171static void
172test_timeouts (void)
173{
174 GMainContext *ctx;
175 GMainLoop *loop;
176 GSource *source;
177
178 if (!g_test_thorough ())
179 {
180 g_test_skip (msg: "Not running timing heavy test");
181 return;
182 }
183
184 a = b = c = 0;
185
186 ctx = g_main_context_new ();
187 loop = g_main_loop_new (context: ctx, FALSE);
188
189 source = g_timeout_source_new (interval: 100);
190 g_source_set_callback (source, func: count_calls, data: &a, NULL);
191 g_source_attach (source, context: ctx);
192 g_source_unref (source);
193
194 source = g_timeout_source_new (interval: 250);
195 g_source_set_callback (source, func: count_calls, data: &b, NULL);
196 g_source_attach (source, context: ctx);
197 g_source_unref (source);
198
199 source = g_timeout_source_new (interval: 330);
200 g_source_set_callback (source, func: count_calls, data: &c, NULL);
201 g_source_attach (source, context: ctx);
202 g_source_unref (source);
203
204 source = g_timeout_source_new (interval: 1050);
205 g_source_set_callback (source, func: (GSourceFunc)g_main_loop_quit, data: loop, NULL);
206 g_source_attach (source, context: ctx);
207 g_source_unref (source);
208
209 g_main_loop_run (loop);
210
211 /* We may be delayed for an arbitrary amount of time - for example,
212 * it's possible for all timeouts to fire exactly once.
213 */
214 g_assert_cmpint (a, >, 0);
215 g_assert_cmpint (a, >=, b);
216 g_assert_cmpint (b, >=, c);
217
218 g_assert_cmpint (a, <=, 10);
219 g_assert_cmpint (b, <=, 4);
220 g_assert_cmpint (c, <=, 3);
221
222 g_main_loop_unref (loop);
223 g_main_context_unref (context: ctx);
224}
225
226static void
227test_priorities (void)
228{
229 GMainContext *ctx;
230 GSource *sourcea;
231 GSource *sourceb;
232
233 a = b = c = 0;
234
235 ctx = g_main_context_new ();
236
237 sourcea = g_idle_source_new ();
238 g_source_set_callback (source: sourcea, func: count_calls, data: &a, NULL);
239 g_source_set_priority (source: sourcea, priority: 1);
240 g_source_attach (source: sourcea, context: ctx);
241 g_source_unref (source: sourcea);
242
243 sourceb = g_idle_source_new ();
244 g_source_set_callback (source: sourceb, func: count_calls, data: &b, NULL);
245 g_source_set_priority (source: sourceb, priority: 0);
246 g_source_attach (source: sourceb, context: ctx);
247 g_source_unref (source: sourceb);
248
249 g_assert_true (g_main_context_pending (ctx));
250 g_assert_true (g_main_context_iteration (ctx, FALSE));
251 g_assert_cmpint (a, ==, 0);
252 g_assert_cmpint (b, ==, 1);
253
254 g_assert_true (g_main_context_iteration (ctx, FALSE));
255 g_assert_cmpint (a, ==, 0);
256 g_assert_cmpint (b, ==, 2);
257
258 g_source_destroy (source: sourceb);
259
260 g_assert_true (g_main_context_iteration (ctx, FALSE));
261 g_assert_cmpint (a, ==, 1);
262 g_assert_cmpint (b, ==, 2);
263
264 g_assert_true (g_main_context_pending (ctx));
265 g_source_destroy (source: sourcea);
266 g_assert_false (g_main_context_pending (ctx));
267
268 g_main_context_unref (context: ctx);
269}
270
271static gboolean
272quit_loop (gpointer data)
273{
274 GMainLoop *loop = data;
275
276 g_main_loop_quit (loop);
277
278 return G_SOURCE_REMOVE;
279}
280
281static gint count;
282
283static gboolean
284func (gpointer data)
285{
286 if (data != NULL)
287 g_assert_true (data == g_thread_self ());
288
289 count++;
290
291 return FALSE;
292}
293
294static gboolean
295call_func (gpointer data)
296{
297 func (data: g_thread_self ());
298
299 return G_SOURCE_REMOVE;
300}
301
302static GMutex mutex;
303static GCond cond;
304static gboolean thread_ready;
305
306static gpointer
307thread_func (gpointer data)
308{
309 GMainContext *ctx = data;
310 GMainLoop *loop;
311 GSource *source;
312
313 g_main_context_push_thread_default (context: ctx);
314 loop = g_main_loop_new (context: ctx, FALSE);
315
316 g_mutex_lock (mutex: &mutex);
317 thread_ready = TRUE;
318 g_cond_signal (cond: &cond);
319 g_mutex_unlock (mutex: &mutex);
320
321 source = g_timeout_source_new (interval: 500);
322 g_source_set_callback (source, func: quit_loop, data: loop, NULL);
323 g_source_attach (source, context: ctx);
324 g_source_unref (source);
325
326 g_main_loop_run (loop);
327
328 g_main_context_pop_thread_default (context: ctx);
329 g_main_loop_unref (loop);
330
331 return NULL;
332}
333
334static void
335test_invoke (void)
336{
337 GMainContext *ctx;
338 GThread *thread;
339
340 count = 0;
341
342 /* this one gets invoked directly */
343 g_main_context_invoke (NULL, function: func, data: g_thread_self ());
344 g_assert_cmpint (count, ==, 1);
345
346 /* invoking out of an idle works too */
347 g_idle_add (function: call_func, NULL);
348 g_main_context_iteration (context: g_main_context_default (), FALSE);
349 g_assert_cmpint (count, ==, 2);
350
351 /* test thread-default forcing the invocation to go
352 * to another thread
353 */
354 ctx = g_main_context_new ();
355 thread = g_thread_new (name: "worker", func: thread_func, data: ctx);
356
357 g_mutex_lock (mutex: &mutex);
358 while (!thread_ready)
359 g_cond_wait (cond: &cond, mutex: &mutex);
360 g_mutex_unlock (mutex: &mutex);
361
362 g_main_context_invoke (context: ctx, function: func, data: thread);
363
364 g_thread_join (thread);
365 g_assert_cmpint (count, ==, 3);
366
367 g_main_context_unref (context: ctx);
368}
369
370/* We can't use timeout sources here because on slow or heavily-loaded
371 * machines, the test program might not get enough cycles to hit the
372 * timeouts at the expected times. So instead we define a source that
373 * is based on the number of GMainContext iterations.
374 */
375
376static gint counter;
377static gint64 last_counter_update;
378
379typedef struct {
380 GSource source;
381 gint interval;
382 gint timeout;
383} CounterSource;
384
385static gboolean
386counter_source_prepare (GSource *source,
387 gint *timeout)
388{
389 CounterSource *csource = (CounterSource *)source;
390 gint64 now;
391
392 now = g_source_get_time (source);
393 if (now != last_counter_update)
394 {
395 last_counter_update = now;
396 counter++;
397 }
398
399 *timeout = 1;
400 return counter >= csource->timeout;
401}
402
403static gboolean
404counter_source_dispatch (GSource *source,
405 GSourceFunc callback,
406 gpointer user_data)
407{
408 CounterSource *csource = (CounterSource *) source;
409 gboolean again;
410
411 again = callback (user_data);
412
413 if (again)
414 csource->timeout = counter + csource->interval;
415
416 return again;
417}
418
419static GSourceFuncs counter_source_funcs = {
420 counter_source_prepare,
421 NULL,
422 counter_source_dispatch,
423 NULL,
424 NULL,
425 NULL
426};
427
428static GSource *
429counter_source_new (gint interval)
430{
431 GSource *source = g_source_new (source_funcs: &counter_source_funcs, struct_size: sizeof (CounterSource));
432 CounterSource *csource = (CounterSource *) source;
433
434 csource->interval = interval;
435 csource->timeout = counter + interval;
436
437 return source;
438}
439
440
441static gboolean
442run_inner_loop (gpointer user_data)
443{
444 GMainContext *ctx = user_data;
445 GMainLoop *inner;
446 GSource *timeout;
447
448 a++;
449
450 inner = g_main_loop_new (context: ctx, FALSE);
451 timeout = counter_source_new (interval: 100);
452 g_source_set_callback (source: timeout, func: quit_loop, data: inner, NULL);
453 g_source_attach (source: timeout, context: ctx);
454 g_source_unref (source: timeout);
455
456 g_main_loop_run (loop: inner);
457 g_main_loop_unref (loop: inner);
458
459 return G_SOURCE_CONTINUE;
460}
461
462static void
463test_child_sources (void)
464{
465 GMainContext *ctx;
466 GMainLoop *loop;
467 GSource *parent, *child_b, *child_c, *end;
468
469 ctx = g_main_context_new ();
470 loop = g_main_loop_new (context: ctx, FALSE);
471
472 a = b = c = 0;
473
474 parent = counter_source_new (interval: 2000);
475 g_source_set_callback (source: parent, func: run_inner_loop, data: ctx, NULL);
476 g_source_set_priority (source: parent, G_PRIORITY_LOW);
477 g_source_attach (source: parent, context: ctx);
478
479 child_b = counter_source_new (interval: 250);
480 g_source_set_callback (source: child_b, func: count_calls, data: &b, NULL);
481 g_source_add_child_source (source: parent, child_source: child_b);
482
483 child_c = counter_source_new (interval: 330);
484 g_source_set_callback (source: child_c, func: count_calls, data: &c, NULL);
485 g_source_set_priority (source: child_c, G_PRIORITY_HIGH);
486 g_source_add_child_source (source: parent, child_source: child_c);
487
488 /* Child sources always have the priority of the parent */
489 g_assert_cmpint (g_source_get_priority (parent), ==, G_PRIORITY_LOW);
490 g_assert_cmpint (g_source_get_priority (child_b), ==, G_PRIORITY_LOW);
491 g_assert_cmpint (g_source_get_priority (child_c), ==, G_PRIORITY_LOW);
492 g_source_set_priority (source: parent, G_PRIORITY_DEFAULT);
493 g_assert_cmpint (g_source_get_priority (parent), ==, G_PRIORITY_DEFAULT);
494 g_assert_cmpint (g_source_get_priority (child_b), ==, G_PRIORITY_DEFAULT);
495 g_assert_cmpint (g_source_get_priority (child_c), ==, G_PRIORITY_DEFAULT);
496
497 end = counter_source_new (interval: 1050);
498 g_source_set_callback (source: end, func: quit_loop, data: loop, NULL);
499 g_source_attach (source: end, context: ctx);
500 g_source_unref (source: end);
501
502 g_main_loop_run (loop);
503
504 /* The parent source's own timeout will never trigger, so "a" will
505 * only get incremented when "b" or "c" does. And when timeouts get
506 * blocked, they still wait the full interval next time rather than
507 * "catching up". So the timing is:
508 *
509 * 250 - b++ -> a++, run_inner_loop
510 * 330 - (c is blocked)
511 * 350 - inner_loop ends
512 * 350 - c++ belatedly -> a++, run_inner_loop
513 * 450 - inner loop ends
514 * 500 - b++ -> a++, run_inner_loop
515 * 600 - inner_loop ends
516 * 680 - c++ -> a++, run_inner_loop
517 * 750 - (b is blocked)
518 * 780 - inner loop ends
519 * 780 - b++ belatedly -> a++, run_inner_loop
520 * 880 - inner loop ends
521 * 1010 - c++ -> a++, run_inner_loop
522 * 1030 - (b is blocked)
523 * 1050 - end runs, quits outer loop, which has no effect yet
524 * 1110 - inner loop ends, a returns, outer loop exits
525 */
526
527 g_assert_cmpint (a, ==, 6);
528 g_assert_cmpint (b, ==, 3);
529 g_assert_cmpint (c, ==, 3);
530
531 g_source_destroy (source: parent);
532 g_source_unref (source: parent);
533 g_source_unref (source: child_b);
534 g_source_unref (source: child_c);
535
536 g_main_loop_unref (loop);
537 g_main_context_unref (context: ctx);
538}
539
540static void
541test_recursive_child_sources (void)
542{
543 GMainContext *ctx;
544 GMainLoop *loop;
545 GSource *parent, *child_b, *child_c, *end;
546
547 ctx = g_main_context_new ();
548 loop = g_main_loop_new (context: ctx, FALSE);
549
550 a = b = c = 0;
551
552 parent = counter_source_new (interval: 500);
553 g_source_set_callback (source: parent, func: count_calls, data: &a, NULL);
554
555 child_b = counter_source_new (interval: 220);
556 g_source_set_callback (source: child_b, func: count_calls, data: &b, NULL);
557 g_source_add_child_source (source: parent, child_source: child_b);
558
559 child_c = counter_source_new (interval: 430);
560 g_source_set_callback (source: child_c, func: count_calls, data: &c, NULL);
561 g_source_add_child_source (source: child_b, child_source: child_c);
562
563 g_source_attach (source: parent, context: ctx);
564
565 end = counter_source_new (interval: 2010);
566 g_source_set_callback (source: end, func: (GSourceFunc)g_main_loop_quit, data: loop, NULL);
567 g_source_attach (source: end, context: ctx);
568 g_source_unref (source: end);
569
570 g_main_loop_run (loop);
571
572 /* Sequence of events:
573 * 220 b (b -> 440, a -> 720)
574 * 430 c (c -> 860, b -> 650, a -> 930)
575 * 650 b (b -> 870, a -> 1150)
576 * 860 c (c -> 1290, b -> 1080, a -> 1360)
577 * 1080 b (b -> 1300, a -> 1580)
578 * 1290 c (c -> 1720, b -> 1510, a -> 1790)
579 * 1510 b (b -> 1730, a -> 2010)
580 * 1720 c (c -> 2150, b -> 1940, a -> 2220)
581 * 1940 b (b -> 2160, a -> 2440)
582 */
583
584 g_assert_cmpint (a, ==, 9);
585 g_assert_cmpint (b, ==, 9);
586 g_assert_cmpint (c, ==, 4);
587
588 g_source_destroy (source: parent);
589 g_source_unref (source: parent);
590 g_source_unref (source: child_b);
591 g_source_unref (source: child_c);
592
593 g_main_loop_unref (loop);
594 g_main_context_unref (context: ctx);
595}
596
597typedef struct {
598 GSource *parent, *old_child, *new_child;
599 GMainLoop *loop;
600} SwappingTestData;
601
602static gboolean
603swap_sources (gpointer user_data)
604{
605 SwappingTestData *data = user_data;
606
607 if (data->old_child)
608 {
609 g_source_remove_child_source (source: data->parent, child_source: data->old_child);
610 g_clear_pointer (&data->old_child, g_source_unref);
611 }
612
613 if (!data->new_child)
614 {
615 data->new_child = g_timeout_source_new (interval: 0);
616 g_source_set_callback (source: data->new_child, func: quit_loop, data: data->loop, NULL);
617 g_source_add_child_source (source: data->parent, child_source: data->new_child);
618 }
619
620 return G_SOURCE_CONTINUE;
621}
622
623static gboolean
624assert_not_reached_callback (gpointer user_data)
625{
626 g_assert_not_reached ();
627
628 return G_SOURCE_REMOVE;
629}
630
631static void
632test_swapping_child_sources (void)
633{
634 GMainContext *ctx;
635 GMainLoop *loop;
636 SwappingTestData data;
637
638 ctx = g_main_context_new ();
639 loop = g_main_loop_new (context: ctx, FALSE);
640
641 data.parent = counter_source_new (interval: 50);
642 data.loop = loop;
643 g_source_set_callback (source: data.parent, func: swap_sources, data: &data, NULL);
644 g_source_attach (source: data.parent, context: ctx);
645
646 data.old_child = counter_source_new (interval: 100);
647 g_source_add_child_source (source: data.parent, child_source: data.old_child);
648 g_source_set_callback (source: data.old_child, func: assert_not_reached_callback, NULL, NULL);
649
650 data.new_child = NULL;
651 g_main_loop_run (loop);
652
653 g_source_destroy (source: data.parent);
654 g_source_unref (source: data.parent);
655 g_source_unref (source: data.new_child);
656
657 g_main_loop_unref (loop);
658 g_main_context_unref (context: ctx);
659}
660
661static gboolean
662add_source_callback (gpointer user_data)
663{
664 GMainLoop *loop = user_data;
665 GSource *self = g_main_current_source (), *child;
666 GIOChannel *io;
667
668 /* It doesn't matter whether this is a valid fd or not; it never
669 * actually gets polled; the test is just checking that
670 * g_source_add_child_source() doesn't crash.
671 */
672 io = g_io_channel_unix_new (fd: 0);
673 child = g_io_create_watch (channel: io, condition: G_IO_IN);
674 g_source_add_child_source (source: self, child_source: child);
675 g_source_unref (source: child);
676 g_io_channel_unref (channel: io);
677
678 g_main_loop_quit (loop);
679 return FALSE;
680}
681
682static void
683test_blocked_child_sources (void)
684{
685 GMainContext *ctx;
686 GMainLoop *loop;
687 GSource *source;
688
689 g_test_bug (bug_uri_snippet: "701283");
690
691 ctx = g_main_context_new ();
692 loop = g_main_loop_new (context: ctx, FALSE);
693
694 source = g_idle_source_new ();
695 g_source_set_callback (source, func: add_source_callback, data: loop, NULL);
696 g_source_attach (source, context: ctx);
697
698 g_main_loop_run (loop);
699
700 g_source_destroy (source);
701 g_source_unref (source);
702
703 g_main_loop_unref (loop);
704 g_main_context_unref (context: ctx);
705}
706
707typedef struct {
708 GMainContext *ctx;
709 GMainLoop *loop;
710
711 GSource *timeout1, *timeout2;
712 gint64 time1;
713G_GNUC_BEGIN_IGNORE_DEPRECATIONS
714 GTimeVal tv; /* needed for g_source_get_current_time() */
715G_GNUC_END_IGNORE_DEPRECATIONS
716} TimeTestData;
717
718static gboolean
719timeout1_callback (gpointer user_data)
720{
721 TimeTestData *data = user_data;
722 GSource *source;
723 gint64 mtime1, mtime2, time2;
724
725 source = g_main_current_source ();
726 g_assert_true (source == data->timeout1);
727
728 if (data->time1 == -1)
729 {
730 /* First iteration */
731 g_assert_false (g_source_is_destroyed (data->timeout2));
732
733 mtime1 = g_get_monotonic_time ();
734 data->time1 = g_source_get_time (source);
735
736G_GNUC_BEGIN_IGNORE_DEPRECATIONS
737 g_source_get_current_time (source, timeval: &data->tv);
738G_GNUC_END_IGNORE_DEPRECATIONS
739
740 /* g_source_get_time() does not change during a single callback */
741 g_usleep (microseconds: 1000000);
742 mtime2 = g_get_monotonic_time ();
743 time2 = g_source_get_time (source);
744
745 g_assert_cmpint (mtime1, <, mtime2);
746 g_assert_cmpint (data->time1, ==, time2);
747 }
748 else
749 {
750G_GNUC_BEGIN_IGNORE_DEPRECATIONS
751 GTimeVal tv;
752G_GNUC_END_IGNORE_DEPRECATIONS
753
754 /* Second iteration */
755 g_assert_true (g_source_is_destroyed (data->timeout2));
756
757 /* g_source_get_time() MAY change between iterations; in this
758 * case we know for sure that it did because of the g_usleep()
759 * last time.
760 */
761 time2 = g_source_get_time (source);
762 g_assert_cmpint (data->time1, <, time2);
763
764G_GNUC_BEGIN_IGNORE_DEPRECATIONS
765 g_source_get_current_time (source, timeval: &tv);
766G_GNUC_END_IGNORE_DEPRECATIONS
767
768 g_assert_true (tv.tv_sec > data->tv.tv_sec ||
769 (tv.tv_sec == data->tv.tv_sec &&
770 tv.tv_usec > data->tv.tv_usec));
771
772 g_main_loop_quit (loop: data->loop);
773 }
774
775 return TRUE;
776}
777
778static gboolean
779timeout2_callback (gpointer user_data)
780{
781 TimeTestData *data = user_data;
782 GSource *source;
783 gint64 time2, time3;
784
785 source = g_main_current_source ();
786 g_assert_true (source == data->timeout2);
787
788 g_assert_false (g_source_is_destroyed (data->timeout1));
789
790 /* g_source_get_time() does not change between different sources in
791 * a single iteration of the mainloop.
792 */
793 time2 = g_source_get_time (source);
794 g_assert_cmpint (data->time1, ==, time2);
795
796 /* The source should still have a valid time even after being
797 * destroyed, since it's currently running.
798 */
799 g_source_destroy (source);
800 time3 = g_source_get_time (source);
801 g_assert_cmpint (time2, ==, time3);
802
803 return FALSE;
804}
805
806static void
807test_source_time (void)
808{
809 TimeTestData data;
810
811 data.ctx = g_main_context_new ();
812 data.loop = g_main_loop_new (context: data.ctx, FALSE);
813
814 data.timeout1 = g_timeout_source_new (interval: 0);
815 g_source_set_callback (source: data.timeout1, func: timeout1_callback, data: &data, NULL);
816 g_source_attach (source: data.timeout1, context: data.ctx);
817
818 data.timeout2 = g_timeout_source_new (interval: 0);
819 g_source_set_callback (source: data.timeout2, func: timeout2_callback, data: &data, NULL);
820 g_source_attach (source: data.timeout2, context: data.ctx);
821
822 data.time1 = -1;
823
824 g_main_loop_run (loop: data.loop);
825
826 g_assert_false (g_source_is_destroyed (data.timeout1));
827 g_assert_true (g_source_is_destroyed (data.timeout2));
828
829 g_source_destroy (source: data.timeout1);
830 g_source_unref (source: data.timeout1);
831 g_source_unref (source: data.timeout2);
832
833 g_main_loop_unref (loop: data.loop);
834 g_main_context_unref (context: data.ctx);
835}
836
837typedef struct {
838 guint outstanding_ops;
839 GMainLoop *loop;
840} TestOverflowData;
841
842static gboolean
843on_source_fired_cb (gpointer user_data)
844{
845 TestOverflowData *data = user_data;
846 GSource *current_source;
847 GMainContext *current_context;
848 guint source_id;
849
850 data->outstanding_ops--;
851
852 current_source = g_main_current_source ();
853 current_context = g_source_get_context (source: current_source);
854 source_id = g_source_get_id (source: current_source);
855 g_assert_nonnull (g_main_context_find_source_by_id (current_context, source_id));
856 g_source_destroy (source: current_source);
857 g_assert_null (g_main_context_find_source_by_id (current_context, source_id));
858
859 if (data->outstanding_ops == 0)
860 g_main_loop_quit (loop: data->loop);
861 return FALSE;
862}
863
864static GSource *
865add_idle_source (GMainContext *ctx,
866 TestOverflowData *data)
867{
868 GSource *source;
869
870 source = g_idle_source_new ();
871 g_source_set_callback (source, func: on_source_fired_cb, data, NULL);
872 g_source_attach (source, context: ctx);
873 g_source_unref (source);
874 data->outstanding_ops++;
875
876 return source;
877}
878
879static void
880test_mainloop_overflow (void)
881{
882 GMainContext *ctx;
883 GMainLoop *loop;
884 GSource *source;
885 TestOverflowData data;
886 guint i;
887
888 g_test_bug (bug_uri_snippet: "687098");
889
890 memset (s: &data, c: 0, n: sizeof (data));
891
892 ctx = GLIB_PRIVATE_CALL (g_main_context_new_with_next_id) (G_MAXUINT-1);
893
894 loop = g_main_loop_new (context: ctx, TRUE);
895 data.outstanding_ops = 0;
896 data.loop = loop;
897
898 source = add_idle_source (ctx, data: &data);
899 g_assert_cmpint (source->source_id, ==, G_MAXUINT-1);
900
901 source = add_idle_source (ctx, data: &data);
902 g_assert_cmpint (source->source_id, ==, G_MAXUINT);
903
904 source = add_idle_source (ctx, data: &data);
905 g_assert_cmpint (source->source_id, !=, 0);
906
907 /* Now, a lot more sources */
908 for (i = 0; i < 50; i++)
909 {
910 source = add_idle_source (ctx, data: &data);
911 g_assert_cmpint (source->source_id, !=, 0);
912 }
913
914 g_main_loop_run (loop);
915 g_assert_cmpint (data.outstanding_ops, ==, 0);
916
917 g_main_loop_unref (loop);
918 g_main_context_unref (context: ctx);
919}
920
921static gint ready_time_dispatched; /* (atomic) */
922
923static gboolean
924ready_time_dispatch (GSource *source,
925 GSourceFunc callback,
926 gpointer user_data)
927{
928 g_atomic_int_set (&ready_time_dispatched, TRUE);
929
930 g_source_set_ready_time (source, ready_time: -1);
931
932 return TRUE;
933}
934
935static gpointer
936run_context (gpointer user_data)
937{
938 g_main_loop_run (loop: user_data);
939
940 return NULL;
941}
942
943static void
944test_ready_time (void)
945{
946 GThread *thread;
947 GSource *source;
948 GSourceFuncs source_funcs = {
949 NULL, NULL, ready_time_dispatch, NULL, NULL, NULL
950 };
951 GMainLoop *loop;
952
953 source = g_source_new (source_funcs: &source_funcs, struct_size: sizeof (GSource));
954 g_source_attach (source, NULL);
955 g_source_unref (source);
956
957 /* Unfortunately we can't do too many things with respect to timing
958 * without getting into trouble on slow systems or heavily loaded
959 * builders.
960 *
961 * We can test that the basics are working, though.
962 */
963
964 /* A source with no ready time set should not fire */
965 g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
966 while (g_main_context_iteration (NULL, FALSE));
967 g_assert_false (g_atomic_int_get (&ready_time_dispatched));
968
969 /* The ready time should not have been changed */
970 g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
971
972 /* Of course this shouldn't change anything either */
973 g_source_set_ready_time (source, ready_time: -1);
974 g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
975
976 /* A source with a ready time set to tomorrow should not fire on any
977 * builder, no matter how badly loaded...
978 */
979 g_source_set_ready_time (source, ready_time: g_get_monotonic_time () + G_TIME_SPAN_DAY);
980 while (g_main_context_iteration (NULL, FALSE));
981 g_assert_false (g_atomic_int_get (&ready_time_dispatched));
982 /* Make sure it didn't get reset */
983 g_assert_cmpint (g_source_get_ready_time (source), !=, -1);
984
985 /* Ready time of -1 -> don't fire */
986 g_source_set_ready_time (source, ready_time: -1);
987 while (g_main_context_iteration (NULL, FALSE));
988 g_assert_false (g_atomic_int_get (&ready_time_dispatched));
989 /* Not reset, but should still be -1 from above */
990 g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
991
992 /* A ready time of the current time should fire immediately */
993 g_source_set_ready_time (source, ready_time: g_get_monotonic_time ());
994 while (g_main_context_iteration (NULL, FALSE));
995 g_assert_true (g_atomic_int_get (&ready_time_dispatched));
996 g_atomic_int_set (&ready_time_dispatched, FALSE);
997 /* Should have gotten reset by the handler function */
998 g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
999
1000 /* As well as one in the recent past... */
1001 g_source_set_ready_time (source, ready_time: g_get_monotonic_time () - G_TIME_SPAN_SECOND);
1002 while (g_main_context_iteration (NULL, FALSE));
1003 g_assert_true (g_atomic_int_get (&ready_time_dispatched));
1004 g_atomic_int_set (&ready_time_dispatched, FALSE);
1005 g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
1006
1007 /* Zero is the 'official' way to get a source to fire immediately */
1008 g_source_set_ready_time (source, ready_time: 0);
1009 while (g_main_context_iteration (NULL, FALSE));
1010 g_assert_true (g_atomic_int_get (&ready_time_dispatched));
1011 g_atomic_int_set (&ready_time_dispatched, FALSE);
1012 g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
1013
1014 /* Now do some tests of cross-thread wakeups.
1015 *
1016 * Make sure it wakes up right away from the start.
1017 */
1018 g_source_set_ready_time (source, ready_time: 0);
1019 loop = g_main_loop_new (NULL, FALSE);
1020 thread = g_thread_new (name: "context thread", func: run_context, data: loop);
1021 while (!g_atomic_int_get (&ready_time_dispatched));
1022
1023 /* Now let's see if it can wake up from sleeping. */
1024 g_usleep (G_TIME_SPAN_SECOND / 2);
1025 g_atomic_int_set (&ready_time_dispatched, FALSE);
1026 g_source_set_ready_time (source, ready_time: 0);
1027 while (!g_atomic_int_get (&ready_time_dispatched));
1028
1029 /* kill the thread */
1030 g_main_loop_quit (loop);
1031 g_thread_join (thread);
1032 g_main_loop_unref (loop);
1033
1034 g_source_destroy (source);
1035}
1036
1037static void
1038test_wakeup(void)
1039{
1040 GMainContext *ctx;
1041 int i;
1042
1043 ctx = g_main_context_new ();
1044
1045 /* run a random large enough number of times because
1046 * main contexts tend to wake up a few times after creation.
1047 */
1048 for (i = 0; i < 100; i++)
1049 {
1050 /* This is the invariant we care about:
1051 * g_main_context_wakeup(ctx,) ensures that the next call to
1052 * g_main_context_iteration (ctx, TRUE) returns and doesn't
1053 * block.
1054 * This is important in threaded apps where we might not know
1055 * if the thread calls g_main_context_wakeup() before or after
1056 * we enter g_main_context_iteration().
1057 */
1058 g_main_context_wakeup (context: ctx);
1059 g_main_context_iteration (context: ctx, TRUE);
1060 }
1061
1062 g_main_context_unref (context: ctx);
1063}
1064
1065static void
1066test_remove_invalid (void)
1067{
1068 g_test_expect_message (log_domain: "GLib", log_level: G_LOG_LEVEL_CRITICAL, pattern: "Source ID 3000000000 was not found*");
1069 g_source_remove (tag: 3000000000u);
1070 g_test_assert_expected_messages ();
1071}
1072
1073static gboolean
1074trivial_prepare (GSource *source,
1075 gint *timeout)
1076{
1077 *timeout = 0;
1078 return TRUE;
1079}
1080
1081static gint n_finalized;
1082
1083static void
1084trivial_finalize (GSource *source)
1085{
1086 n_finalized++;
1087}
1088
1089static void
1090test_unref_while_pending (void)
1091{
1092 static GSourceFuncs funcs = {
1093 trivial_prepare, NULL, NULL, trivial_finalize, NULL, NULL
1094 };
1095 GMainContext *context;
1096 GSource *source;
1097
1098 context = g_main_context_new ();
1099
1100 source = g_source_new (source_funcs: &funcs, struct_size: sizeof (GSource));
1101 g_source_attach (source, context);
1102 g_source_unref (source);
1103
1104 /* Do incomplete main iteration -- get a pending source but don't dispatch it. */
1105 g_main_context_prepare (context, NULL);
1106 g_main_context_query (context, max_priority: 0, NULL, NULL, n_fds: 0);
1107 g_main_context_check (context, max_priority: 1000, NULL, n_fds: 0);
1108
1109 /* Destroy the context */
1110 g_main_context_unref (context);
1111
1112 /* Make sure we didn't leak the source */
1113 g_assert_cmpint (n_finalized, ==, 1);
1114}
1115
1116#ifdef G_OS_UNIX
1117
1118#include <glib-unix.h>
1119#include <unistd.h>
1120
1121static gchar zeros[1024];
1122
1123static gsize
1124fill_a_pipe (gint fd)
1125{
1126 gsize written = 0;
1127 GPollFD pfd;
1128
1129 pfd.fd = fd;
1130 pfd.events = G_IO_OUT;
1131 while (g_poll (fds: &pfd, nfds: 1, timeout: 0) == 1)
1132 /* we should never see -1 here */
1133 written += write (fd: fd, buf: zeros, n: sizeof zeros);
1134
1135 return written;
1136}
1137
1138static gboolean
1139write_bytes (gint fd,
1140 GIOCondition condition,
1141 gpointer user_data)
1142{
1143 gssize *to_write = user_data;
1144 gint limit;
1145
1146 if (*to_write == 0)
1147 return FALSE;
1148
1149 /* Detect if we run before we should */
1150 g_assert_cmpint (*to_write, >=, 0);
1151
1152 limit = MIN ((gsize) *to_write, sizeof zeros);
1153 *to_write -= write (fd: fd, buf: zeros, n: limit);
1154
1155 return TRUE;
1156}
1157
1158static gboolean
1159read_bytes (gint fd,
1160 GIOCondition condition,
1161 gpointer user_data)
1162{
1163 static gchar buffer[1024];
1164 gssize *to_read = user_data;
1165
1166 *to_read -= read (fd: fd, buf: buffer, nbytes: sizeof buffer);
1167
1168 /* The loop will exit when there is nothing else to read, then we will
1169 * use g_source_remove() to destroy this source.
1170 */
1171 return TRUE;
1172}
1173
1174#ifdef G_OS_UNIX
1175static void
1176test_unix_fd (void)
1177{
1178 gssize to_write = -1;
1179 gssize to_read;
1180 gint fds[2];
1181 gint a, b;
1182 gint s;
1183 GSource *source_a;
1184 GSource *source_b;
1185
1186 s = pipe (pipedes: fds);
1187 g_assert_cmpint (s, ==, 0);
1188
1189 to_read = fill_a_pipe (fd: fds[1]);
1190 /* write at higher priority to keep the pipe full... */
1191 a = g_unix_fd_add_full (G_PRIORITY_HIGH, fd: fds[1], condition: G_IO_OUT, function: write_bytes, user_data: &to_write, NULL);
1192 source_a = g_source_ref (source: g_main_context_find_source_by_id (NULL, source_id: a));
1193 /* make sure no 'writes' get dispatched yet */
1194 while (g_main_context_iteration (NULL, FALSE));
1195
1196 to_read += 128 * 1024 * 1024;
1197 to_write = 128 * 1024 * 1024;
1198 b = g_unix_fd_add (fd: fds[0], condition: G_IO_IN, function: read_bytes, user_data: &to_read);
1199 source_b = g_source_ref (source: g_main_context_find_source_by_id (NULL, source_id: b));
1200
1201 /* Assuming the kernel isn't internally 'laggy' then there will always
1202 * be either data to read or room in which to write. That will keep
1203 * the loop running until all data has been read and written.
1204 */
1205 while (TRUE)
1206 {
1207 gssize to_write_was = to_write;
1208 gssize to_read_was = to_read;
1209
1210 if (!g_main_context_iteration (NULL, FALSE))
1211 break;
1212
1213 /* Since the sources are at different priority, only one of them
1214 * should possibly have run.
1215 */
1216 g_assert_true (to_write == to_write_was || to_read == to_read_was);
1217 }
1218
1219 g_assert_cmpint (to_write, ==, 0);
1220 g_assert_cmpint (to_read, ==, 0);
1221
1222 /* 'a' is already removed by itself */
1223 g_assert_true (g_source_is_destroyed (source_a));
1224 g_source_unref (source: source_a);
1225 g_source_remove (tag: b);
1226 g_assert_true (g_source_is_destroyed (source_b));
1227 g_source_unref (source: source_b);
1228 close (fd: fds[1]);
1229 close (fd: fds[0]);
1230}
1231#endif
1232
1233static void
1234assert_main_context_state (gint n_to_poll,
1235 ...)
1236{
1237 GMainContext *context;
1238 gboolean consumed[10] = { };
1239 GPollFD poll_fds[10];
1240 gboolean acquired;
1241 gboolean immediate;
1242 gint max_priority;
1243 gint timeout;
1244 gint n;
1245 gint i, j;
1246 va_list ap;
1247
1248 context = g_main_context_default ();
1249
1250 acquired = g_main_context_acquire (context);
1251 g_assert_true (acquired);
1252
1253 immediate = g_main_context_prepare (context, priority: &max_priority);
1254 g_assert_false (immediate);
1255 n = g_main_context_query (context, max_priority, timeout_: &timeout, fds: poll_fds, n_fds: 10);
1256 g_assert_cmpint (n, ==, n_to_poll + 1); /* one will be the gwakeup */
1257
1258 va_start (ap, n_to_poll);
1259 for (i = 0; i < n_to_poll; i++)
1260 {
1261 gint expected_fd = va_arg (ap, gint);
1262 GIOCondition expected_events = va_arg (ap, GIOCondition);
1263 GIOCondition report_events = va_arg (ap, GIOCondition);
1264
1265 for (j = 0; j < n; j++)
1266 if (!consumed[j] && poll_fds[j].fd == expected_fd && poll_fds[j].events == expected_events)
1267 {
1268 poll_fds[j].revents = report_events;
1269 consumed[j] = TRUE;
1270 break;
1271 }
1272
1273 if (j == n)
1274 g_error ("Unable to find fd %d (index %d) with events 0x%x", expected_fd, i, (guint) expected_events);
1275 }
1276 va_end (ap);
1277
1278 /* find the gwakeup, flag as non-ready */
1279 for (i = 0; i < n; i++)
1280 if (!consumed[i])
1281 poll_fds[i].revents = 0;
1282
1283 if (g_main_context_check (context, max_priority, fds: poll_fds, n_fds: n))
1284 g_main_context_dispatch (context);
1285
1286 g_main_context_release (context);
1287}
1288
1289static gboolean
1290flag_bool (gint fd,
1291 GIOCondition condition,
1292 gpointer user_data)
1293{
1294 gboolean *flag = user_data;
1295
1296 *flag = TRUE;
1297
1298 return TRUE;
1299}
1300
1301static void
1302test_unix_fd_source (void)
1303{
1304 GSource *out_source;
1305 GSource *in_source;
1306 GSource *source;
1307 gboolean out, in;
1308 gint fds[2];
1309 gint s;
1310
1311 assert_main_context_state (n_to_poll: 0);
1312
1313 s = pipe (pipedes: fds);
1314 g_assert_cmpint (s, ==, 0);
1315
1316 source = g_unix_fd_source_new (fd: fds[1], condition: G_IO_OUT);
1317 g_source_attach (source, NULL);
1318
1319 /* Check that a source with no callback gets successfully detached
1320 * with a warning printed.
1321 */
1322 g_test_expect_message (log_domain: "GLib", log_level: G_LOG_LEVEL_WARNING, pattern: "*GUnixFDSource dispatched without callback*");
1323 while (g_main_context_iteration (NULL, FALSE));
1324 g_test_assert_expected_messages ();
1325 g_assert_true (g_source_is_destroyed (source));
1326 g_source_unref (source);
1327
1328 out = in = FALSE;
1329 out_source = g_unix_fd_source_new (fd: fds[1], condition: G_IO_OUT);
1330 /* -Wcast-function-type complains about casting 'flag_bool' to GSourceFunc.
1331 * GCC has no way of knowing that it will be cast back to GUnixFDSourceFunc
1332 * before being called. Although GLib itself is not compiled with
1333 * -Wcast-function-type, applications that use GLib may well be (since
1334 * -Wextra includes it), so we provide a G_SOURCE_FUNC() macro to suppress
1335 * the warning. We check that it works here.
1336 */
1337#if G_GNUC_CHECK_VERSION(8, 0)
1338#pragma GCC diagnostic push
1339#pragma GCC diagnostic error "-Wcast-function-type"
1340#endif
1341 g_source_set_callback (source: out_source, G_SOURCE_FUNC (flag_bool), data: &out, NULL);
1342#if G_GNUC_CHECK_VERSION(8, 0)
1343#pragma GCC diagnostic pop
1344#endif
1345 g_source_attach (source: out_source, NULL);
1346 assert_main_context_state (n_to_poll: 1,
1347 fds[1], G_IO_OUT, 0);
1348 g_assert_true (!in && !out);
1349
1350 in_source = g_unix_fd_source_new (fd: fds[0], condition: G_IO_IN);
1351 g_source_set_callback (source: in_source, func: (GSourceFunc) flag_bool, data: &in, NULL);
1352 g_source_set_priority (source: in_source, G_PRIORITY_DEFAULT_IDLE);
1353 g_source_attach (source: in_source, NULL);
1354 assert_main_context_state (n_to_poll: 2,
1355 fds[0], G_IO_IN, G_IO_IN,
1356 fds[1], G_IO_OUT, G_IO_OUT);
1357 /* out is higher priority so only it should fire */
1358 g_assert_true (!in && out);
1359
1360 /* raise the priority of the in source to higher than out*/
1361 in = out = FALSE;
1362 g_source_set_priority (source: in_source, G_PRIORITY_HIGH);
1363 assert_main_context_state (n_to_poll: 2,
1364 fds[0], G_IO_IN, G_IO_IN,
1365 fds[1], G_IO_OUT, G_IO_OUT);
1366 g_assert_true (in && !out);
1367
1368 /* now, let them be equal */
1369 in = out = FALSE;
1370 g_source_set_priority (source: in_source, G_PRIORITY_DEFAULT);
1371 assert_main_context_state (n_to_poll: 2,
1372 fds[0], G_IO_IN, G_IO_IN,
1373 fds[1], G_IO_OUT, G_IO_OUT);
1374 g_assert_true (in && out);
1375
1376 g_source_destroy (source: out_source);
1377 g_source_unref (source: out_source);
1378 g_source_destroy (source: in_source);
1379 g_source_unref (source: in_source);
1380 close (fd: fds[1]);
1381 close (fd: fds[0]);
1382}
1383
1384typedef struct
1385{
1386 GSource parent;
1387 gboolean flagged;
1388} FlagSource;
1389
1390static gboolean
1391return_true (GSource *source, GSourceFunc callback, gpointer user_data)
1392{
1393 FlagSource *flag_source = (FlagSource *) source;
1394
1395 flag_source->flagged = TRUE;
1396
1397 return TRUE;
1398}
1399
1400#define assert_flagged(s) g_assert_true (((FlagSource *) (s))->flagged);
1401#define assert_not_flagged(s) g_assert_true (!((FlagSource *) (s))->flagged);
1402#define clear_flag(s) ((FlagSource *) (s))->flagged = 0
1403
1404static void
1405test_source_unix_fd_api (void)
1406{
1407 GSourceFuncs no_funcs = {
1408 NULL, NULL, return_true, NULL, NULL, NULL
1409 };
1410 GSource *source_a;
1411 GSource *source_b;
1412 gpointer tag1, tag2;
1413 gint fds_a[2];
1414 gint fds_b[2];
1415
1416 pipe (pipedes: fds_a);
1417 pipe (pipedes: fds_b);
1418
1419 source_a = g_source_new (source_funcs: &no_funcs, struct_size: sizeof (FlagSource));
1420 source_b = g_source_new (source_funcs: &no_funcs, struct_size: sizeof (FlagSource));
1421
1422 /* attach a source with more than one fd */
1423 g_source_add_unix_fd (source: source_a, fd: fds_a[0], events: G_IO_IN);
1424 g_source_add_unix_fd (source: source_a, fd: fds_a[1], events: G_IO_OUT);
1425 g_source_attach (source: source_a, NULL);
1426 assert_main_context_state (n_to_poll: 2,
1427 fds_a[0], G_IO_IN, 0,
1428 fds_a[1], G_IO_OUT, 0);
1429 assert_not_flagged (source_a);
1430
1431 /* attach a higher priority source with no fds */
1432 g_source_set_priority (source: source_b, G_PRIORITY_HIGH);
1433 g_source_attach (source: source_b, NULL);
1434 assert_main_context_state (n_to_poll: 2,
1435 fds_a[0], G_IO_IN, G_IO_IN,
1436 fds_a[1], G_IO_OUT, 0);
1437 assert_flagged (source_a);
1438 assert_not_flagged (source_b);
1439 clear_flag (source_a);
1440
1441 /* add some fds to the second source, while attached */
1442 tag1 = g_source_add_unix_fd (source: source_b, fd: fds_b[0], events: G_IO_IN);
1443 tag2 = g_source_add_unix_fd (source: source_b, fd: fds_b[1], events: G_IO_OUT);
1444 assert_main_context_state (n_to_poll: 4,
1445 fds_a[0], G_IO_IN, 0,
1446 fds_a[1], G_IO_OUT, G_IO_OUT,
1447 fds_b[0], G_IO_IN, 0,
1448 fds_b[1], G_IO_OUT, G_IO_OUT);
1449 /* only 'b' (higher priority) should have dispatched */
1450 assert_not_flagged (source_a);
1451 assert_flagged (source_b);
1452 clear_flag (source_b);
1453
1454 /* change our events on b to the same as they were before */
1455 g_source_modify_unix_fd (source: source_b, tag: tag1, new_events: G_IO_IN);
1456 g_source_modify_unix_fd (source: source_b, tag: tag2, new_events: G_IO_OUT);
1457 assert_main_context_state (n_to_poll: 4,
1458 fds_a[0], G_IO_IN, 0,
1459 fds_a[1], G_IO_OUT, G_IO_OUT,
1460 fds_b[0], G_IO_IN, 0,
1461 fds_b[1], G_IO_OUT, G_IO_OUT);
1462 assert_not_flagged (source_a);
1463 assert_flagged (source_b);
1464 clear_flag (source_b);
1465
1466 /* now reverse them */
1467 g_source_modify_unix_fd (source: source_b, tag: tag1, new_events: G_IO_OUT);
1468 g_source_modify_unix_fd (source: source_b, tag: tag2, new_events: G_IO_IN);
1469 assert_main_context_state (n_to_poll: 4,
1470 fds_a[0], G_IO_IN, 0,
1471 fds_a[1], G_IO_OUT, G_IO_OUT,
1472 fds_b[0], G_IO_OUT, 0,
1473 fds_b[1], G_IO_IN, 0);
1474 /* 'b' had no events, so 'a' can go this time */
1475 assert_flagged (source_a);
1476 assert_not_flagged (source_b);
1477 clear_flag (source_a);
1478
1479 /* remove one of the fds from 'b' */
1480 g_source_remove_unix_fd (source: source_b, tag: tag1);
1481 assert_main_context_state (n_to_poll: 3,
1482 fds_a[0], G_IO_IN, 0,
1483 fds_a[1], G_IO_OUT, 0,
1484 fds_b[1], G_IO_IN, 0);
1485 assert_not_flagged (source_a);
1486 assert_not_flagged (source_b);
1487
1488 /* remove the other */
1489 g_source_remove_unix_fd (source: source_b, tag: tag2);
1490 assert_main_context_state (n_to_poll: 2,
1491 fds_a[0], G_IO_IN, 0,
1492 fds_a[1], G_IO_OUT, 0);
1493 assert_not_flagged (source_a);
1494 assert_not_flagged (source_b);
1495
1496 /* destroy the sources */
1497 g_source_destroy (source: source_a);
1498 g_source_destroy (source: source_b);
1499 assert_main_context_state (n_to_poll: 0);
1500
1501 g_source_unref (source: source_a);
1502 g_source_unref (source: source_b);
1503 close (fd: fds_a[0]);
1504 close (fd: fds_a[1]);
1505 close (fd: fds_b[0]);
1506 close (fd: fds_b[1]);
1507}
1508
1509static gboolean
1510unixfd_quit_loop (gint fd,
1511 GIOCondition condition,
1512 gpointer user_data)
1513{
1514 GMainLoop *loop = user_data;
1515
1516 g_main_loop_quit (loop);
1517
1518 return FALSE;
1519}
1520
1521static void
1522test_unix_file_poll (void)
1523{
1524 gint fd;
1525 GSource *source;
1526 GMainLoop *loop;
1527
1528 fd = open (file: "/dev/null", O_RDONLY);
1529 g_assert_cmpint (fd, >=, 0);
1530
1531 loop = g_main_loop_new (NULL, FALSE);
1532
1533 source = g_unix_fd_source_new (fd, condition: G_IO_IN);
1534 g_source_set_callback (source, func: (GSourceFunc) unixfd_quit_loop, data: loop, NULL);
1535 g_source_attach (source, NULL);
1536
1537 /* Should not block */
1538 g_main_loop_run (loop);
1539
1540 g_source_destroy (source);
1541
1542 assert_main_context_state (n_to_poll: 0);
1543
1544 g_source_unref (source);
1545
1546 g_main_loop_unref (loop);
1547
1548 close (fd: fd);
1549}
1550
1551static void
1552test_unix_fd_priority (void)
1553{
1554 gint fd1, fd2;
1555 GMainLoop *loop;
1556 GSource *source;
1557
1558 gint s1 = 0;
1559 gboolean s2 = FALSE, s3 = FALSE;
1560
1561 g_test_bug (bug_uri_snippet: "https://gitlab.gnome.org/GNOME/glib/-/issues/1592");
1562
1563 loop = g_main_loop_new (NULL, FALSE);
1564
1565 source = g_idle_source_new ();
1566 g_source_set_callback (source, func: count_calls, data: &s1, NULL);
1567 g_source_set_priority (source, priority: 0);
1568 g_source_attach (source, NULL);
1569 g_source_unref (source);
1570
1571 fd1 = open (file: "/dev/random", O_RDONLY);
1572 g_assert_cmpint (fd1, >=, 0);
1573 source = g_unix_fd_source_new (fd: fd1, condition: G_IO_IN);
1574 g_source_set_callback (source, G_SOURCE_FUNC (flag_bool), data: &s2, NULL);
1575 g_source_set_priority (source, priority: 10);
1576 g_source_attach (source, NULL);
1577 g_source_unref (source);
1578
1579 fd2 = open (file: "/dev/random", O_RDONLY);
1580 g_assert_cmpint (fd2, >=, 0);
1581 source = g_unix_fd_source_new (fd: fd2, condition: G_IO_IN);
1582 g_source_set_callback (source, G_SOURCE_FUNC (flag_bool), data: &s3, NULL);
1583 g_source_set_priority (source, priority: 0);
1584 g_source_attach (source, NULL);
1585 g_source_unref (source);
1586
1587 /* This tests a bug that depends on the source with the lowest FD
1588 identifier to have the lowest priority. Make sure that this is
1589 the case. */
1590 g_assert_cmpint (fd1, <, fd2);
1591
1592 g_assert_true (g_main_context_iteration (NULL, FALSE));
1593
1594 /* Idle source should have been dispatched. */
1595 g_assert_cmpint (s1, ==, 1);
1596 /* Low priority FD source shouldn't have been dispatched. */
1597 g_assert_false (s2);
1598 /* Default priority FD source should have been dispatched. */
1599 g_assert_true (s3);
1600
1601 g_main_loop_unref (loop);
1602
1603 close (fd: fd1);
1604 close (fd: fd2);
1605}
1606
1607#endif
1608
1609#ifdef G_OS_UNIX
1610static gboolean
1611timeout_cb (gpointer data)
1612{
1613 GMainLoop *loop = data;
1614 GMainContext *context;
1615
1616 context = g_main_loop_get_context (loop);
1617 g_assert_true (g_main_loop_is_running (loop));
1618 g_assert_true (g_main_context_is_owner (context));
1619
1620 g_main_loop_quit (loop);
1621
1622 return G_SOURCE_REMOVE;
1623}
1624
1625static gpointer
1626threadf (gpointer data)
1627{
1628 GMainContext *context = data;
1629 GMainLoop *loop;
1630 GSource *source;
1631
1632 loop = g_main_loop_new (context, FALSE);
1633 source = g_timeout_source_new (interval: 250);
1634 g_source_set_callback (source, func: timeout_cb, data: loop, NULL);
1635 g_source_attach (source, context);
1636 g_source_unref (source);
1637
1638 g_main_loop_run (loop);
1639
1640 g_main_loop_unref (loop);
1641
1642 return NULL;
1643}
1644
1645static void
1646test_mainloop_wait (void)
1647{
1648#ifdef _GLIB_ADDRESS_SANITIZER
1649 (void) threadf;
1650 g_test_incomplete ("FIXME: Leaks a GMainLoop, see glib#2307");
1651#else
1652 GMainContext *context;
1653 GThread *t1, *t2;
1654
1655 context = g_main_context_new ();
1656
1657 t1 = g_thread_new (name: "t1", func: threadf, data: context);
1658 t2 = g_thread_new (name: "t2", func: threadf, data: context);
1659
1660 g_thread_join (thread: t1);
1661 g_thread_join (thread: t2);
1662
1663 g_main_context_unref (context);
1664#endif
1665}
1666#endif
1667
1668static gboolean
1669nfds_in_cb (GIOChannel *io,
1670 GIOCondition condition,
1671 gpointer user_data)
1672{
1673 gboolean *in_cb_ran = user_data;
1674
1675 *in_cb_ran = TRUE;
1676 g_assert_cmpint (condition, ==, G_IO_IN);
1677 return FALSE;
1678}
1679
1680static gboolean
1681nfds_out_cb (GIOChannel *io,
1682 GIOCondition condition,
1683 gpointer user_data)
1684{
1685 gboolean *out_cb_ran = user_data;
1686
1687 *out_cb_ran = TRUE;
1688 g_assert_cmpint (condition, ==, G_IO_OUT);
1689 return FALSE;
1690}
1691
1692static gboolean
1693nfds_out_low_cb (GIOChannel *io,
1694 GIOCondition condition,
1695 gpointer user_data)
1696{
1697 g_assert_not_reached ();
1698 return FALSE;
1699}
1700
1701static void
1702test_nfds (void)
1703{
1704 GMainContext *ctx;
1705 GPollFD out_fds[3];
1706 gint fd, nfds;
1707 GIOChannel *io;
1708 GSource *source1, *source2, *source3;
1709 gboolean source1_ran = FALSE, source3_ran = FALSE;
1710 gchar *tmpfile;
1711 GError *error = NULL;
1712
1713 ctx = g_main_context_new ();
1714 nfds = g_main_context_query (context: ctx, G_MAXINT, NULL,
1715 fds: out_fds, G_N_ELEMENTS (out_fds));
1716 /* An "empty" GMainContext will have a single GPollFD, for its
1717 * internal GWakeup.
1718 */
1719 g_assert_cmpint (nfds, ==, 1);
1720
1721 fd = g_file_open_tmp (NULL, name_used: &tmpfile, error: &error);
1722 g_assert_no_error (error);
1723
1724 io = g_io_channel_unix_new (fd);
1725#ifdef G_OS_WIN32
1726 /* The fd in the pollfds won't be the same fd we passed in */
1727 g_io_channel_win32_make_pollfd (io, G_IO_IN, out_fds);
1728 fd = out_fds[0].fd;
1729#endif
1730
1731 /* Add our first pollfd */
1732 source1 = g_io_create_watch (channel: io, condition: G_IO_IN);
1733 g_source_set_priority (source: source1, G_PRIORITY_DEFAULT);
1734 g_source_set_callback (source: source1, func: (GSourceFunc) nfds_in_cb,
1735 data: &source1_ran, NULL);
1736 g_source_attach (source: source1, context: ctx);
1737
1738 nfds = g_main_context_query (context: ctx, G_MAXINT, NULL,
1739 fds: out_fds, G_N_ELEMENTS (out_fds));
1740 g_assert_cmpint (nfds, ==, 2);
1741 if (out_fds[0].fd == fd)
1742 g_assert_cmpint (out_fds[0].events, ==, G_IO_IN);
1743 else if (out_fds[1].fd == fd)
1744 g_assert_cmpint (out_fds[1].events, ==, G_IO_IN);
1745 else
1746 g_assert_not_reached ();
1747
1748 /* Add a second pollfd with the same fd but different event, and
1749 * lower priority.
1750 */
1751 source2 = g_io_create_watch (channel: io, condition: G_IO_OUT);
1752 g_source_set_priority (source: source2, G_PRIORITY_LOW);
1753 g_source_set_callback (source: source2, func: (GSourceFunc) nfds_out_low_cb,
1754 NULL, NULL);
1755 g_source_attach (source: source2, context: ctx);
1756
1757 /* g_main_context_query() should still return only 2 pollfds,
1758 * one of which has our fd, and a combined events field.
1759 */
1760 nfds = g_main_context_query (context: ctx, G_MAXINT, NULL,
1761 fds: out_fds, G_N_ELEMENTS (out_fds));
1762 g_assert_cmpint (nfds, ==, 2);
1763 if (out_fds[0].fd == fd)
1764 g_assert_cmpint (out_fds[0].events, ==, G_IO_IN | G_IO_OUT);
1765 else if (out_fds[1].fd == fd)
1766 g_assert_cmpint (out_fds[1].events, ==, G_IO_IN | G_IO_OUT);
1767 else
1768 g_assert_not_reached ();
1769
1770 /* But if we query with a max priority, we won't see the
1771 * lower-priority one.
1772 */
1773 nfds = g_main_context_query (context: ctx, G_PRIORITY_DEFAULT, NULL,
1774 fds: out_fds, G_N_ELEMENTS (out_fds));
1775 g_assert_cmpint (nfds, ==, 2);
1776 if (out_fds[0].fd == fd)
1777 g_assert_cmpint (out_fds[0].events, ==, G_IO_IN);
1778 else if (out_fds[1].fd == fd)
1779 g_assert_cmpint (out_fds[1].events, ==, G_IO_IN);
1780 else
1781 g_assert_not_reached ();
1782
1783 /* Third pollfd */
1784 source3 = g_io_create_watch (channel: io, condition: G_IO_OUT);
1785 g_source_set_priority (source: source3, G_PRIORITY_DEFAULT);
1786 g_source_set_callback (source: source3, func: (GSourceFunc) nfds_out_cb,
1787 data: &source3_ran, NULL);
1788 g_source_attach (source: source3, context: ctx);
1789
1790 nfds = g_main_context_query (context: ctx, G_MAXINT, NULL,
1791 fds: out_fds, G_N_ELEMENTS (out_fds));
1792 g_assert_cmpint (nfds, ==, 2);
1793 if (out_fds[0].fd == fd)
1794 g_assert_cmpint (out_fds[0].events, ==, G_IO_IN | G_IO_OUT);
1795 else if (out_fds[1].fd == fd)
1796 g_assert_cmpint (out_fds[1].events, ==, G_IO_IN | G_IO_OUT);
1797 else
1798 g_assert_not_reached ();
1799
1800 /* Now actually iterate the loop; the fd should be readable and
1801 * writable, so source1 and source3 should be triggered, but *not*
1802 * source2, since it's lower priority than them. (Though on
1803 * G_OS_WIN32, source3 doesn't get triggered, probably because of
1804 * giowin32 weirdness...)
1805 */
1806 g_main_context_iteration (context: ctx, FALSE);
1807
1808 g_assert_true (source1_ran);
1809#ifndef G_OS_WIN32
1810 g_assert_true (source3_ran);
1811#endif
1812
1813 g_source_destroy (source: source1);
1814 g_source_unref (source: source1);
1815 g_source_destroy (source: source2);
1816 g_source_unref (source: source2);
1817 g_source_destroy (source: source3);
1818 g_source_unref (source: source3);
1819
1820 g_io_channel_unref (channel: io);
1821 remove (filename: tmpfile);
1822 g_free (mem: tmpfile);
1823
1824 g_main_context_unref (context: ctx);
1825}
1826
1827static gboolean source_finalize_called = FALSE;
1828static guint source_dispose_called = 0;
1829static gboolean source_dispose_recycle = FALSE;
1830
1831static void
1832finalize (GSource *source)
1833{
1834 g_assert_false (source_finalize_called);
1835 source_finalize_called = TRUE;
1836}
1837
1838static void
1839dispose (GSource *source)
1840{
1841 /* Dispose must always be called before finalize */
1842 g_assert_false (source_finalize_called);
1843
1844 if (source_dispose_recycle)
1845 g_source_ref (source);
1846 source_dispose_called++;
1847}
1848
1849static GSourceFuncs source_funcs = {
1850 prepare,
1851 check,
1852 dispatch,
1853 finalize,
1854 NULL,
1855 NULL
1856};
1857
1858static void
1859test_maincontext_source_finalization (void)
1860{
1861 GSource *source;
1862
1863 /* Check if GSource destruction without dispose function works and calls the
1864 * finalize function as expected */
1865 source_finalize_called = FALSE;
1866 source_dispose_called = 0;
1867 source_dispose_recycle = FALSE;
1868 source = g_source_new (source_funcs: &source_funcs, struct_size: sizeof (GSource));
1869 g_source_unref (source);
1870 g_assert_cmpint (source_dispose_called, ==, 0);
1871 g_assert_true (source_finalize_called);
1872
1873 /* Check if GSource destruction with dispose function works and calls the
1874 * dispose and finalize function as expected */
1875 source_finalize_called = FALSE;
1876 source_dispose_called = 0;
1877 source_dispose_recycle = FALSE;
1878 source = g_source_new (source_funcs: &source_funcs, struct_size: sizeof (GSource));
1879 g_source_set_dispose_function (source, dispose);
1880 g_source_unref (source);
1881 g_assert_cmpint (source_dispose_called, ==, 1);
1882 g_assert_true (source_finalize_called);
1883
1884 /* Check if GSource destruction with dispose function works and recycling
1885 * the source from dispose works without calling the finalize function */
1886 source_finalize_called = FALSE;
1887 source_dispose_called = 0;
1888 source_dispose_recycle = TRUE;
1889 source = g_source_new (source_funcs: &source_funcs, struct_size: sizeof (GSource));
1890 g_source_set_dispose_function (source, dispose);
1891 g_source_unref (source);
1892 g_assert_cmpint (source_dispose_called, ==, 1);
1893 g_assert_false (source_finalize_called);
1894
1895 /* Check if the source is properly recycled */
1896 g_assert_cmpint (source->ref_count, ==, 1);
1897
1898 /* And then get rid of it properly */
1899 source_dispose_recycle = FALSE;
1900 g_source_unref (source);
1901 g_assert_cmpint (source_dispose_called, ==, 2);
1902 g_assert_true (source_finalize_called);
1903}
1904
1905/* GSource implementation which optionally keeps a strong reference to another
1906 * GSource until finalization, when it destroys and unrefs the other source.
1907 */
1908typedef struct {
1909 GSource source;
1910
1911 GSource *other_source;
1912} SourceWithSource;
1913
1914static void
1915finalize_source_with_source (GSource *source)
1916{
1917 SourceWithSource *s = (SourceWithSource *) source;
1918
1919 if (s->other_source)
1920 {
1921 g_source_destroy (source: s->other_source);
1922 g_source_unref (source: s->other_source);
1923 s->other_source = NULL;
1924 }
1925}
1926
1927static GSourceFuncs source_with_source_funcs = {
1928 NULL,
1929 NULL,
1930 NULL,
1931 finalize_source_with_source,
1932 NULL,
1933 NULL
1934};
1935
1936static void
1937test_maincontext_source_finalization_from_source (gconstpointer user_data)
1938{
1939 GMainContext *c = g_main_context_new ();
1940 GSource *s1, *s2;
1941
1942 g_test_summary (summary: "Tests if freeing a GSource as part of another GSource "
1943 "during main context destruction works.");
1944 g_test_bug (bug_uri_snippet: "https://gitlab.gnome.org/GNOME/glib/merge_requests/1353");
1945
1946 s1 = g_source_new (source_funcs: &source_with_source_funcs, struct_size: sizeof (SourceWithSource));
1947 s2 = g_source_new (source_funcs: &source_with_source_funcs, struct_size: sizeof (SourceWithSource));
1948 ((SourceWithSource *)s1)->other_source = g_source_ref (source: s2);
1949
1950 /* Attach sources in a different order so they end up being destroyed
1951 * in a different order by the main context */
1952 if (GPOINTER_TO_INT (user_data) < 5)
1953 {
1954 g_source_attach (source: s1, context: c);
1955 g_source_attach (source: s2, context: c);
1956 }
1957 else
1958 {
1959 g_source_attach (source: s2, context: c);
1960 g_source_attach (source: s1, context: c);
1961 }
1962
1963 /* Test a few different permutations here */
1964 if (GPOINTER_TO_INT (user_data) % 5 == 0)
1965 {
1966 /* Get rid of our references so that destroying the context
1967 * will unref them immediately */
1968 g_source_unref (source: s1);
1969 g_source_unref (source: s2);
1970 g_main_context_unref (context: c);
1971 }
1972 else if (GPOINTER_TO_INT (user_data) % 5 == 1)
1973 {
1974 /* Destroy and free the sources before the context */
1975 g_source_destroy (source: s1);
1976 g_source_unref (source: s1);
1977 g_source_destroy (source: s2);
1978 g_source_unref (source: s2);
1979 g_main_context_unref (context: c);
1980 }
1981 else if (GPOINTER_TO_INT (user_data) % 5 == 2)
1982 {
1983 /* Destroy and free the sources before the context */
1984 g_source_destroy (source: s2);
1985 g_source_unref (source: s2);
1986 g_source_destroy (source: s1);
1987 g_source_unref (source: s1);
1988 g_main_context_unref (context: c);
1989 }
1990 else if (GPOINTER_TO_INT (user_data) % 5 == 3)
1991 {
1992 /* Destroy and free the context before the sources */
1993 g_main_context_unref (context: c);
1994 g_source_unref (source: s2);
1995 g_source_unref (source: s1);
1996 }
1997 else if (GPOINTER_TO_INT (user_data) % 5 == 4)
1998 {
1999 /* Destroy and free the context before the sources */
2000 g_main_context_unref (context: c);
2001 g_source_unref (source: s1);
2002 g_source_unref (source: s2);
2003 }
2004}
2005
2006static gboolean
2007dispatch_source_with_source (GSource *source, GSourceFunc callback, gpointer user_data)
2008{
2009 return G_SOURCE_REMOVE;
2010}
2011
2012static GSourceFuncs source_with_source_funcs_dispatch = {
2013 NULL,
2014 NULL,
2015 dispatch_source_with_source,
2016 finalize_source_with_source,
2017 NULL,
2018 NULL
2019};
2020
2021static void
2022test_maincontext_source_finalization_from_dispatch (gconstpointer user_data)
2023{
2024 GMainContext *c = g_main_context_new ();
2025 GSource *s1, *s2;
2026
2027 g_test_summary (summary: "Tests if freeing a GSource as part of another GSource "
2028 "during main context iteration works.");
2029
2030 s1 = g_source_new (source_funcs: &source_with_source_funcs_dispatch, struct_size: sizeof (SourceWithSource));
2031 s2 = g_source_new (source_funcs: &source_with_source_funcs_dispatch, struct_size: sizeof (SourceWithSource));
2032 ((SourceWithSource *)s1)->other_source = g_source_ref (source: s2);
2033
2034 g_source_attach (source: s1, context: c);
2035 g_source_attach (source: s2, context: c);
2036
2037 if (GPOINTER_TO_INT (user_data) == 0)
2038 {
2039 /* This finalizes s1 as part of the iteration, which then destroys and
2040 * frees s2 too */
2041 g_source_set_ready_time (source: s1, ready_time: 0);
2042 }
2043 else if (GPOINTER_TO_INT (user_data) == 1)
2044 {
2045 /* This destroys s2 as part of the iteration but does not free it as
2046 * it's still referenced by s1 */
2047 g_source_set_ready_time (source: s2, ready_time: 0);
2048 }
2049 else if (GPOINTER_TO_INT (user_data) == 2)
2050 {
2051 /* This destroys both s1 and s2 as part of the iteration */
2052 g_source_set_ready_time (source: s1, ready_time: 0);
2053 g_source_set_ready_time (source: s2, ready_time: 0);
2054 }
2055
2056 /* Get rid of our references so only the main context has one now */
2057 g_source_unref (source: s1);
2058 g_source_unref (source: s2);
2059
2060 /* Iterate as long as there are sources to dispatch */
2061 while (g_main_context_iteration (context: c, FALSE))
2062 {
2063 /* Do nothing here */
2064 }
2065
2066 g_main_context_unref (context: c);
2067}
2068
2069int
2070main (int argc, char *argv[])
2071{
2072 gint i;
2073
2074 g_test_init (argc: &argc, argv: &argv, NULL);
2075 g_test_bug_base (uri_pattern: "http://bugzilla.gnome.org/");
2076
2077 g_test_add_func (testpath: "/maincontext/basic", test_func: test_maincontext_basic);
2078 g_test_add_func (testpath: "/maincontext/source_finalization", test_func: test_maincontext_source_finalization);
2079 for (i = 0; i < 10; i++)
2080 {
2081 gchar *name = g_strdup_printf (format: "/maincontext/source_finalization_from_source/%d", i);
2082 g_test_add_data_func (testpath: name, GINT_TO_POINTER (i), test_func: test_maincontext_source_finalization_from_source);
2083 g_free (mem: name);
2084 }
2085 for (i = 0; i < 3; i++)
2086 {
2087 gchar *name = g_strdup_printf (format: "/maincontext/source_finalization_from_dispatch/%d", i);
2088 g_test_add_data_func (testpath: name, GINT_TO_POINTER (i), test_func: test_maincontext_source_finalization_from_dispatch);
2089 g_free (mem: name);
2090 }
2091 g_test_add_func (testpath: "/mainloop/basic", test_func: test_mainloop_basic);
2092 g_test_add_func (testpath: "/mainloop/timeouts", test_func: test_timeouts);
2093 g_test_add_func (testpath: "/mainloop/priorities", test_func: test_priorities);
2094 g_test_add_func (testpath: "/mainloop/invoke", test_func: test_invoke);
2095 g_test_add_func (testpath: "/mainloop/child_sources", test_func: test_child_sources);
2096 g_test_add_func (testpath: "/mainloop/recursive_child_sources", test_func: test_recursive_child_sources);
2097 g_test_add_func (testpath: "/mainloop/swapping_child_sources", test_func: test_swapping_child_sources);
2098 g_test_add_func (testpath: "/mainloop/blocked_child_sources", test_func: test_blocked_child_sources);
2099 g_test_add_func (testpath: "/mainloop/source_time", test_func: test_source_time);
2100 g_test_add_func (testpath: "/mainloop/overflow", test_func: test_mainloop_overflow);
2101 g_test_add_func (testpath: "/mainloop/ready-time", test_func: test_ready_time);
2102 g_test_add_func (testpath: "/mainloop/wakeup", test_func: test_wakeup);
2103 g_test_add_func (testpath: "/mainloop/remove-invalid", test_func: test_remove_invalid);
2104 g_test_add_func (testpath: "/mainloop/unref-while-pending", test_func: test_unref_while_pending);
2105#ifdef G_OS_UNIX
2106 g_test_add_func (testpath: "/mainloop/unix-fd", test_func: test_unix_fd);
2107 g_test_add_func (testpath: "/mainloop/unix-fd-source", test_func: test_unix_fd_source);
2108 g_test_add_func (testpath: "/mainloop/source-unix-fd-api", test_func: test_source_unix_fd_api);
2109 g_test_add_func (testpath: "/mainloop/wait", test_func: test_mainloop_wait);
2110 g_test_add_func (testpath: "/mainloop/unix-file-poll", test_func: test_unix_file_poll);
2111 g_test_add_func (testpath: "/mainloop/unix-fd-priority", test_func: test_unix_fd_priority);
2112#endif
2113 g_test_add_func (testpath: "/mainloop/nfds", test_func: test_nfds);
2114
2115 return g_test_run ();
2116}
2117

source code of gtk/subprojects/glib/glib/tests/mainloop.c