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