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 | |
28 | static gboolean |
29 | cb (gpointer data) |
30 | { |
31 | return FALSE; |
32 | } |
33 | |
34 | static gboolean |
35 | prepare (GSource *source, gint *time) |
36 | { |
37 | return FALSE; |
38 | } |
39 | static gboolean |
40 | check (GSource *source) |
41 | { |
42 | return FALSE; |
43 | } |
44 | static gboolean |
45 | dispatch (GSource *source, GSourceFunc cb, gpointer date) |
46 | { |
47 | return FALSE; |
48 | } |
49 | |
50 | static GSourceFuncs funcs = { |
51 | prepare, |
52 | check, |
53 | dispatch, |
54 | NULL, |
55 | NULL, |
56 | NULL |
57 | }; |
58 | |
59 | static void |
60 | test_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 | |
135 | static void |
136 | test_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 | |
157 | static gint a; |
158 | static gint b; |
159 | static gint c; |
160 | |
161 | static gboolean |
162 | count_calls (gpointer data) |
163 | { |
164 | gint *i = data; |
165 | |
166 | (*i)++; |
167 | |
168 | return TRUE; |
169 | } |
170 | |
171 | static void |
172 | test_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 | |
226 | static void |
227 | test_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 | |
271 | static gboolean |
272 | quit_loop (gpointer data) |
273 | { |
274 | GMainLoop *loop = data; |
275 | |
276 | g_main_loop_quit (loop); |
277 | |
278 | return G_SOURCE_REMOVE; |
279 | } |
280 | |
281 | static gint count; |
282 | |
283 | static gboolean |
284 | func (gpointer data) |
285 | { |
286 | if (data != NULL) |
287 | g_assert_true (data == g_thread_self ()); |
288 | |
289 | count++; |
290 | |
291 | return FALSE; |
292 | } |
293 | |
294 | static gboolean |
295 | call_func (gpointer data) |
296 | { |
297 | func (data: g_thread_self ()); |
298 | |
299 | return G_SOURCE_REMOVE; |
300 | } |
301 | |
302 | static GMutex mutex; |
303 | static GCond cond; |
304 | static gboolean thread_ready; |
305 | |
306 | static gpointer |
307 | thread_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 | |
334 | static void |
335 | test_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 | |
376 | static gint counter; |
377 | static gint64 last_counter_update; |
378 | |
379 | typedef struct { |
380 | GSource source; |
381 | gint interval; |
382 | gint timeout; |
383 | } CounterSource; |
384 | |
385 | static gboolean |
386 | counter_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 | |
403 | static gboolean |
404 | counter_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 | |
419 | static GSourceFuncs counter_source_funcs = { |
420 | counter_source_prepare, |
421 | NULL, |
422 | counter_source_dispatch, |
423 | NULL, |
424 | NULL, |
425 | NULL |
426 | }; |
427 | |
428 | static GSource * |
429 | counter_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 | |
441 | static gboolean |
442 | run_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 | |
462 | static void |
463 | test_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 | |
540 | static void |
541 | test_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 | |
597 | typedef struct { |
598 | GSource *parent, *old_child, *new_child; |
599 | GMainLoop *loop; |
600 | } SwappingTestData; |
601 | |
602 | static gboolean |
603 | swap_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 | |
623 | static gboolean |
624 | assert_not_reached_callback (gpointer user_data) |
625 | { |
626 | g_assert_not_reached (); |
627 | |
628 | return G_SOURCE_REMOVE; |
629 | } |
630 | |
631 | static void |
632 | test_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 | |
661 | static gboolean |
662 | add_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 | |
682 | static void |
683 | test_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 | |
707 | typedef struct { |
708 | GMainContext *ctx; |
709 | GMainLoop *loop; |
710 | |
711 | GSource *timeout1, *timeout2; |
712 | gint64 time1; |
713 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
714 | GTimeVal tv; /* needed for g_source_get_current_time() */ |
715 | G_GNUC_END_IGNORE_DEPRECATIONS |
716 | } TimeTestData; |
717 | |
718 | static gboolean |
719 | timeout1_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 | |
736 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
737 | g_source_get_current_time (source, timeval: &data->tv); |
738 | G_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 | { |
750 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
751 | GTimeVal tv; |
752 | G_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 | |
764 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
765 | g_source_get_current_time (source, timeval: &tv); |
766 | G_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 | |
778 | static gboolean |
779 | timeout2_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 | |
806 | static void |
807 | test_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 | |
837 | typedef struct { |
838 | guint outstanding_ops; |
839 | GMainLoop *loop; |
840 | } TestOverflowData; |
841 | |
842 | static gboolean |
843 | on_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 | |
864 | static GSource * |
865 | add_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 | |
879 | static void |
880 | test_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 | |
921 | static gint ready_time_dispatched; /* (atomic) */ |
922 | |
923 | static gboolean |
924 | ready_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 | |
935 | static gpointer |
936 | run_context (gpointer user_data) |
937 | { |
938 | g_main_loop_run (loop: user_data); |
939 | |
940 | return NULL; |
941 | } |
942 | |
943 | static void |
944 | test_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 | |
1037 | static void |
1038 | test_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 | |
1065 | static void |
1066 | test_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 | |
1073 | static gboolean |
1074 | trivial_prepare (GSource *source, |
1075 | gint *timeout) |
1076 | { |
1077 | *timeout = 0; |
1078 | return TRUE; |
1079 | } |
1080 | |
1081 | static gint n_finalized; |
1082 | |
1083 | static void |
1084 | trivial_finalize (GSource *source) |
1085 | { |
1086 | n_finalized++; |
1087 | } |
1088 | |
1089 | static void |
1090 | test_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 | |
1121 | static gchar zeros[1024]; |
1122 | |
1123 | static gsize |
1124 | fill_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 | |
1138 | static gboolean |
1139 | write_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 | |
1158 | static gboolean |
1159 | read_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 |
1175 | static void |
1176 | test_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 | |
1233 | static void |
1234 | assert_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 | |
1289 | static gboolean |
1290 | flag_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 | |
1301 | static void |
1302 | test_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 | |
1384 | typedef struct |
1385 | { |
1386 | GSource parent; |
1387 | gboolean flagged; |
1388 | } FlagSource; |
1389 | |
1390 | static gboolean |
1391 | return_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 | |
1404 | static void |
1405 | test_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 | |
1509 | static gboolean |
1510 | unixfd_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 | |
1521 | static void |
1522 | test_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 | |
1551 | static void |
1552 | test_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 |
1610 | static gboolean |
1611 | timeout_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 | |
1625 | static gpointer |
1626 | threadf (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 | |
1645 | static void |
1646 | test_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 | |
1668 | static gboolean |
1669 | nfds_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 | |
1680 | static gboolean |
1681 | nfds_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 | |
1692 | static gboolean |
1693 | nfds_out_low_cb (GIOChannel *io, |
1694 | GIOCondition condition, |
1695 | gpointer user_data) |
1696 | { |
1697 | g_assert_not_reached (); |
1698 | return FALSE; |
1699 | } |
1700 | |
1701 | static void |
1702 | test_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 | |
1827 | static gboolean source_finalize_called = FALSE; |
1828 | static guint source_dispose_called = 0; |
1829 | static gboolean source_dispose_recycle = FALSE; |
1830 | |
1831 | static void |
1832 | finalize (GSource *source) |
1833 | { |
1834 | g_assert_false (source_finalize_called); |
1835 | source_finalize_called = TRUE; |
1836 | } |
1837 | |
1838 | static void |
1839 | dispose (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 | |
1849 | static GSourceFuncs source_funcs = { |
1850 | prepare, |
1851 | check, |
1852 | dispatch, |
1853 | finalize, |
1854 | NULL, |
1855 | NULL |
1856 | }; |
1857 | |
1858 | static void |
1859 | test_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 | */ |
1908 | typedef struct { |
1909 | GSource source; |
1910 | |
1911 | GSource *other_source; |
1912 | } SourceWithSource; |
1913 | |
1914 | static void |
1915 | finalize_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 | |
1927 | static GSourceFuncs source_with_source_funcs = { |
1928 | NULL, |
1929 | NULL, |
1930 | NULL, |
1931 | finalize_source_with_source, |
1932 | NULL, |
1933 | NULL |
1934 | }; |
1935 | |
1936 | static void |
1937 | test_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 | |
2006 | static gboolean |
2007 | dispatch_source_with_source (GSource *source, GSourceFunc callback, gpointer user_data) |
2008 | { |
2009 | return G_SOURCE_REMOVE; |
2010 | } |
2011 | |
2012 | static 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 | |
2021 | static void |
2022 | test_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 | |
2069 | int |
2070 | main (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 | |