1 | /* |
2 | * Copyright 2012-2019 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * See the included COPYING file for more information. |
10 | */ |
11 | |
12 | #include <gio/gio.h> |
13 | #include <string.h> |
14 | |
15 | static GMainLoop *loop; |
16 | static GThread *main_thread; |
17 | static gssize magic; |
18 | |
19 | /* We need objects for a few tests where we don't care what type |
20 | * they are, just that they're GObjects. |
21 | */ |
22 | #define g_dummy_object_new g_socket_client_new |
23 | |
24 | static gboolean |
25 | idle_quit_loop (gpointer user_data) |
26 | { |
27 | g_main_loop_quit (loop); |
28 | return FALSE; |
29 | } |
30 | |
31 | static void |
32 | completed_cb (GObject *gobject, |
33 | GParamSpec *pspec, |
34 | gpointer user_data) |
35 | { |
36 | gboolean *notification_emitted = user_data; |
37 | *notification_emitted = TRUE; |
38 | } |
39 | |
40 | static void |
41 | wait_for_completed_notification (GTask *task) |
42 | { |
43 | gboolean notification_emitted = FALSE; |
44 | gboolean is_completed = FALSE; |
45 | |
46 | /* Hold a ref. so we can check the :completed property afterwards. */ |
47 | g_object_ref (task); |
48 | |
49 | g_signal_connect (task, "notify::completed" , |
50 | (GCallback) completed_cb, ¬ification_emitted); |
51 | g_idle_add (function: idle_quit_loop, NULL); |
52 | g_main_loop_run (loop); |
53 | g_assert_true (notification_emitted); |
54 | |
55 | g_assert_true (g_task_get_completed (task)); |
56 | g_object_get (G_OBJECT (task), first_property_name: "completed" , &is_completed, NULL); |
57 | g_assert_true (is_completed); |
58 | |
59 | g_object_unref (object: task); |
60 | } |
61 | |
62 | /* test_basic */ |
63 | |
64 | static void |
65 | basic_callback (GObject *object, |
66 | GAsyncResult *result, |
67 | gpointer user_data) |
68 | { |
69 | gssize *result_out = user_data; |
70 | GError *error = NULL; |
71 | |
72 | g_assert (object == NULL); |
73 | g_assert (g_task_is_valid (result, object)); |
74 | g_assert (g_async_result_get_user_data (result) == user_data); |
75 | g_assert (!g_task_had_error (G_TASK (result))); |
76 | g_assert_false (g_task_get_completed (G_TASK (result))); |
77 | |
78 | *result_out = g_task_propagate_int (G_TASK (result), error: &error); |
79 | g_assert_no_error (error); |
80 | |
81 | g_assert (!g_task_had_error (G_TASK (result))); |
82 | |
83 | g_main_loop_quit (loop); |
84 | } |
85 | |
86 | static gboolean |
87 | basic_return (gpointer user_data) |
88 | { |
89 | GTask *task = user_data; |
90 | |
91 | g_task_return_int (task, result: magic); |
92 | g_object_unref (object: task); |
93 | |
94 | return FALSE; |
95 | } |
96 | |
97 | static void |
98 | basic_destroy_notify (gpointer user_data) |
99 | { |
100 | gboolean *destroyed = user_data; |
101 | |
102 | *destroyed = TRUE; |
103 | } |
104 | |
105 | static void |
106 | test_basic (void) |
107 | { |
108 | GTask *task; |
109 | gssize result; |
110 | gboolean task_data_destroyed = FALSE; |
111 | gboolean notification_emitted = FALSE; |
112 | |
113 | task = g_task_new (NULL, NULL, callback: basic_callback, callback_data: &result); |
114 | g_task_set_task_data (task, task_data: &task_data_destroyed, task_data_destroy: basic_destroy_notify); |
115 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
116 | g_signal_connect (task, "notify::completed" , |
117 | (GCallback) completed_cb, ¬ification_emitted); |
118 | |
119 | g_idle_add (function: basic_return, data: task); |
120 | g_main_loop_run (loop); |
121 | |
122 | g_assert_cmpint (result, ==, magic); |
123 | g_assert (task_data_destroyed == TRUE); |
124 | g_assert_true (notification_emitted); |
125 | g_assert (task == NULL); |
126 | } |
127 | |
128 | /* test_error */ |
129 | |
130 | static void |
131 | error_callback (GObject *object, |
132 | GAsyncResult *result, |
133 | gpointer user_data) |
134 | { |
135 | gssize *result_out = user_data; |
136 | GError *error = NULL; |
137 | |
138 | g_assert (object == NULL); |
139 | g_assert (g_task_is_valid (result, object)); |
140 | g_assert (g_async_result_get_user_data (result) == user_data); |
141 | g_assert (g_task_had_error (G_TASK (result))); |
142 | g_assert_false (g_task_get_completed (G_TASK (result))); |
143 | |
144 | *result_out = g_task_propagate_int (G_TASK (result), error: &error); |
145 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); |
146 | g_error_free (error); |
147 | |
148 | g_assert (g_task_had_error (G_TASK (result))); |
149 | |
150 | g_main_loop_quit (loop); |
151 | } |
152 | |
153 | static gboolean |
154 | error_return (gpointer user_data) |
155 | { |
156 | GTask *task = user_data; |
157 | |
158 | g_task_return_new_error (task, |
159 | G_IO_ERROR, code: G_IO_ERROR_FAILED, |
160 | format: "Failed" ); |
161 | g_object_unref (object: task); |
162 | |
163 | return FALSE; |
164 | } |
165 | |
166 | static void |
167 | error_destroy_notify (gpointer user_data) |
168 | { |
169 | gboolean *destroyed = user_data; |
170 | |
171 | *destroyed = TRUE; |
172 | } |
173 | |
174 | static void |
175 | test_error (void) |
176 | { |
177 | GTask *task; |
178 | gssize result; |
179 | gboolean first_task_data_destroyed = FALSE; |
180 | gboolean second_task_data_destroyed = FALSE; |
181 | gboolean notification_emitted = FALSE; |
182 | |
183 | task = g_task_new (NULL, NULL, callback: error_callback, callback_data: &result); |
184 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
185 | g_signal_connect (task, "notify::completed" , |
186 | (GCallback) completed_cb, ¬ification_emitted); |
187 | |
188 | g_assert (first_task_data_destroyed == FALSE); |
189 | g_task_set_task_data (task, task_data: &first_task_data_destroyed, task_data_destroy: error_destroy_notify); |
190 | g_assert (first_task_data_destroyed == FALSE); |
191 | |
192 | /* Calling g_task_set_task_data() again will destroy the first data */ |
193 | g_task_set_task_data (task, task_data: &second_task_data_destroyed, task_data_destroy: error_destroy_notify); |
194 | g_assert (first_task_data_destroyed == TRUE); |
195 | g_assert (second_task_data_destroyed == FALSE); |
196 | |
197 | g_idle_add (function: error_return, data: task); |
198 | g_main_loop_run (loop); |
199 | |
200 | g_assert_cmpint (result, ==, -1); |
201 | g_assert (second_task_data_destroyed == TRUE); |
202 | g_assert_true (notification_emitted); |
203 | g_assert (task == NULL); |
204 | } |
205 | |
206 | /* test_return_from_same_iteration: calling g_task_return_* from the |
207 | * loop iteration the task was created in defers completion until the |
208 | * next iteration. |
209 | */ |
210 | gboolean same_result = FALSE; |
211 | gboolean same_notification_emitted = FALSE; |
212 | |
213 | static void |
214 | same_callback (GObject *object, |
215 | GAsyncResult *result, |
216 | gpointer user_data) |
217 | { |
218 | gboolean *result_out = user_data; |
219 | GError *error = NULL; |
220 | |
221 | g_assert (object == NULL); |
222 | g_assert (g_task_is_valid (result, object)); |
223 | g_assert (g_async_result_get_user_data (result) == user_data); |
224 | g_assert (!g_task_had_error (G_TASK (result))); |
225 | g_assert_false (g_task_get_completed (G_TASK (result))); |
226 | |
227 | *result_out = g_task_propagate_boolean (G_TASK (result), error: &error); |
228 | g_assert_no_error (error); |
229 | |
230 | g_assert (!g_task_had_error (G_TASK (result))); |
231 | |
232 | g_main_loop_quit (loop); |
233 | } |
234 | |
235 | static gboolean |
236 | same_start (gpointer user_data) |
237 | { |
238 | gpointer *weak_pointer = user_data; |
239 | GTask *task; |
240 | |
241 | task = g_task_new (NULL, NULL, callback: same_callback, callback_data: &same_result); |
242 | *weak_pointer = task; |
243 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: weak_pointer); |
244 | g_signal_connect (task, "notify::completed" , |
245 | (GCallback) completed_cb, &same_notification_emitted); |
246 | |
247 | g_task_return_boolean (task, TRUE); |
248 | g_object_unref (object: task); |
249 | |
250 | /* same_callback should not have been invoked yet */ |
251 | g_assert (same_result == FALSE); |
252 | g_assert (*weak_pointer == task); |
253 | g_assert_false (same_notification_emitted); |
254 | |
255 | return FALSE; |
256 | } |
257 | |
258 | static void |
259 | test_return_from_same_iteration (void) |
260 | { |
261 | gpointer weak_pointer; |
262 | |
263 | g_idle_add (function: same_start, data: &weak_pointer); |
264 | g_main_loop_run (loop); |
265 | |
266 | g_assert (same_result == TRUE); |
267 | g_assert (weak_pointer == NULL); |
268 | g_assert_true (same_notification_emitted); |
269 | } |
270 | |
271 | /* test_return_from_toplevel: calling g_task_return_* from outside any |
272 | * main loop completes the task inside the main loop. |
273 | */ |
274 | gboolean toplevel_notification_emitted = FALSE; |
275 | |
276 | static void |
277 | toplevel_callback (GObject *object, |
278 | GAsyncResult *result, |
279 | gpointer user_data) |
280 | { |
281 | gboolean *result_out = user_data; |
282 | GError *error = NULL; |
283 | |
284 | g_assert (object == NULL); |
285 | g_assert (g_task_is_valid (result, object)); |
286 | g_assert (g_async_result_get_user_data (result) == user_data); |
287 | g_assert (!g_task_had_error (G_TASK (result))); |
288 | g_assert_false (g_task_get_completed (G_TASK (result))); |
289 | |
290 | *result_out = g_task_propagate_boolean (G_TASK (result), error: &error); |
291 | g_assert_no_error (error); |
292 | |
293 | g_assert (!g_task_had_error (G_TASK (result))); |
294 | |
295 | g_main_loop_quit (loop); |
296 | } |
297 | |
298 | static void |
299 | test_return_from_toplevel (void) |
300 | { |
301 | GTask *task; |
302 | gboolean result = FALSE; |
303 | |
304 | task = g_task_new (NULL, NULL, callback: toplevel_callback, callback_data: &result); |
305 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
306 | g_signal_connect (task, "notify::completed" , |
307 | (GCallback) completed_cb, &toplevel_notification_emitted); |
308 | |
309 | g_task_return_boolean (task, TRUE); |
310 | g_object_unref (object: task); |
311 | |
312 | /* toplevel_callback should not have been invoked yet */ |
313 | g_assert (result == FALSE); |
314 | g_assert (task != NULL); |
315 | g_assert_false (toplevel_notification_emitted); |
316 | |
317 | g_main_loop_run (loop); |
318 | |
319 | g_assert (result == TRUE); |
320 | g_assert (task == NULL); |
321 | g_assert_true (toplevel_notification_emitted); |
322 | } |
323 | |
324 | /* test_return_from_anon_thread: calling g_task_return_* from a |
325 | * thread with no thread-default main context will complete the |
326 | * task in the task's context/thread. |
327 | */ |
328 | |
329 | gboolean anon_thread_notification_emitted = FALSE; |
330 | GThread *anon_thread; |
331 | |
332 | static void |
333 | anon_callback (GObject *object, |
334 | GAsyncResult *result, |
335 | gpointer user_data) |
336 | { |
337 | gssize *result_out = user_data; |
338 | GError *error = NULL; |
339 | |
340 | g_assert (object == NULL); |
341 | g_assert (g_task_is_valid (result, object)); |
342 | g_assert (g_async_result_get_user_data (result) == user_data); |
343 | g_assert (!g_task_had_error (G_TASK (result))); |
344 | g_assert_false (g_task_get_completed (G_TASK (result))); |
345 | |
346 | g_assert (g_thread_self () == main_thread); |
347 | |
348 | *result_out = g_task_propagate_int (G_TASK (result), error: &error); |
349 | g_assert_no_error (error); |
350 | |
351 | g_assert (!g_task_had_error (G_TASK (result))); |
352 | |
353 | g_main_loop_quit (loop); |
354 | } |
355 | |
356 | static gpointer |
357 | anon_thread_func (gpointer user_data) |
358 | { |
359 | GTask *task = user_data; |
360 | |
361 | g_task_return_int (task, result: magic); |
362 | g_object_unref (object: task); |
363 | |
364 | return NULL; |
365 | } |
366 | |
367 | static gboolean |
368 | anon_start (gpointer user_data) |
369 | { |
370 | GTask *task = user_data; |
371 | |
372 | anon_thread = g_thread_new (name: "test_return_from_anon_thread" , |
373 | func: anon_thread_func, data: task); |
374 | return FALSE; |
375 | } |
376 | |
377 | static void |
378 | test_return_from_anon_thread (void) |
379 | { |
380 | GTask *task; |
381 | gssize result = 0; |
382 | |
383 | task = g_task_new (NULL, NULL, callback: anon_callback, callback_data: &result); |
384 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
385 | g_signal_connect (task, "notify::completed" , |
386 | (GCallback) completed_cb, |
387 | &anon_thread_notification_emitted); |
388 | |
389 | g_idle_add (function: anon_start, data: task); |
390 | g_main_loop_run (loop); |
391 | |
392 | g_thread_join (thread: anon_thread); |
393 | |
394 | g_assert_cmpint (result, ==, magic); |
395 | g_assert (task == NULL); |
396 | g_assert_true (anon_thread_notification_emitted); |
397 | } |
398 | |
399 | /* test_return_from_wrong_thread: calling g_task_return_* from a |
400 | * thread with its own thread-default main context will complete the |
401 | * task in the task's context/thread. |
402 | */ |
403 | |
404 | gboolean wrong_thread_notification_emitted = FALSE; |
405 | GThread *wrong_thread; |
406 | |
407 | static void |
408 | wrong_callback (GObject *object, |
409 | GAsyncResult *result, |
410 | gpointer user_data) |
411 | { |
412 | gssize *result_out = user_data; |
413 | GError *error = NULL; |
414 | |
415 | g_assert (object == NULL); |
416 | g_assert (g_task_is_valid (result, object)); |
417 | g_assert (g_async_result_get_user_data (result) == user_data); |
418 | g_assert (!g_task_had_error (G_TASK (result))); |
419 | g_assert_false (g_task_get_completed (G_TASK (result))); |
420 | |
421 | g_assert (g_thread_self () == main_thread); |
422 | |
423 | *result_out = g_task_propagate_int (G_TASK (result), error: &error); |
424 | g_assert_no_error (error); |
425 | |
426 | g_assert (!g_task_had_error (G_TASK (result))); |
427 | |
428 | g_main_loop_quit (loop); |
429 | } |
430 | |
431 | static gpointer |
432 | wrong_thread_func (gpointer user_data) |
433 | { |
434 | GTask *task = user_data; |
435 | GMainContext *context; |
436 | |
437 | context = g_main_context_new (); |
438 | g_main_context_push_thread_default (context); |
439 | |
440 | g_assert (g_task_get_context (task) != context); |
441 | |
442 | g_task_return_int (task, result: magic); |
443 | g_object_unref (object: task); |
444 | |
445 | g_main_context_pop_thread_default (context); |
446 | g_main_context_unref (context); |
447 | |
448 | return NULL; |
449 | } |
450 | |
451 | static gboolean |
452 | wrong_start (gpointer user_data) |
453 | { |
454 | GTask *task = user_data; |
455 | |
456 | wrong_thread = g_thread_new (name: "test_return_from_anon_thread" , |
457 | func: wrong_thread_func, data: task); |
458 | return FALSE; |
459 | } |
460 | |
461 | static void |
462 | test_return_from_wrong_thread (void) |
463 | { |
464 | GTask *task; |
465 | gssize result = 0; |
466 | |
467 | task = g_task_new (NULL, NULL, callback: wrong_callback, callback_data: &result); |
468 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
469 | g_signal_connect (task, "notify::completed" , |
470 | (GCallback) completed_cb, |
471 | &wrong_thread_notification_emitted); |
472 | |
473 | g_idle_add (function: wrong_start, data: task); |
474 | g_main_loop_run (loop); |
475 | |
476 | g_thread_join (thread: wrong_thread); |
477 | |
478 | g_assert_cmpint (result, ==, magic); |
479 | g_assert (task == NULL); |
480 | g_assert_true (wrong_thread_notification_emitted); |
481 | } |
482 | |
483 | /* test_no_callback */ |
484 | |
485 | static void |
486 | test_no_callback (void) |
487 | { |
488 | GTask *task; |
489 | |
490 | task = g_task_new (NULL, NULL, NULL, NULL); |
491 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
492 | |
493 | g_task_return_boolean (task, TRUE); |
494 | g_object_unref (object: task); |
495 | |
496 | /* Even though there’s no callback, the :completed notification has to |
497 | * happen in an idle handler. */ |
498 | g_assert_nonnull (task); |
499 | wait_for_completed_notification (task); |
500 | g_assert_null (task); |
501 | } |
502 | |
503 | /* test_report_error */ |
504 | |
505 | static void test_report_error (void); |
506 | gboolean error_notification_emitted = FALSE; |
507 | |
508 | static void |
509 | report_callback (GObject *object, |
510 | GAsyncResult *result, |
511 | gpointer user_data) |
512 | { |
513 | gpointer *weak_pointer = user_data; |
514 | GError *error = NULL; |
515 | gssize ret; |
516 | |
517 | g_assert (object == NULL); |
518 | g_assert (g_task_is_valid (result, object)); |
519 | g_assert (g_async_result_get_user_data (result) == user_data); |
520 | g_assert (g_async_result_is_tagged (result, test_report_error)); |
521 | g_assert (g_task_get_source_tag (G_TASK (result)) == test_report_error); |
522 | g_assert (g_task_had_error (G_TASK (result))); |
523 | g_assert_false (g_task_get_completed (G_TASK (result))); |
524 | |
525 | ret = g_task_propagate_int (G_TASK (result), error: &error); |
526 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); |
527 | g_assert_cmpint (ret, ==, -1); |
528 | g_error_free (error); |
529 | |
530 | g_assert (g_task_had_error (G_TASK (result))); |
531 | |
532 | *weak_pointer = result; |
533 | g_object_add_weak_pointer (G_OBJECT (result), weak_pointer_location: weak_pointer); |
534 | g_signal_connect (result, "notify::completed" , |
535 | (GCallback) completed_cb, &error_notification_emitted); |
536 | |
537 | g_main_loop_quit (loop); |
538 | } |
539 | |
540 | static void |
541 | test_report_error (void) |
542 | { |
543 | gpointer weak_pointer = (gpointer)-1; |
544 | |
545 | g_task_report_new_error (NULL, callback: report_callback, callback_data: &weak_pointer, |
546 | source_tag: test_report_error, |
547 | G_IO_ERROR, code: G_IO_ERROR_FAILED, |
548 | format: "Failed" ); |
549 | g_main_loop_run (loop); |
550 | |
551 | g_assert (weak_pointer == NULL); |
552 | g_assert_true (error_notification_emitted); |
553 | } |
554 | |
555 | /* test_priority: tasks complete in priority order */ |
556 | |
557 | static int counter = 0; |
558 | |
559 | static void |
560 | priority_callback (GObject *object, |
561 | GAsyncResult *result, |
562 | gpointer user_data) |
563 | { |
564 | gssize *ret_out = user_data; |
565 | GError *error = NULL; |
566 | |
567 | g_assert (object == NULL); |
568 | g_assert (g_task_is_valid (result, object)); |
569 | g_assert (g_async_result_get_user_data (result) == user_data); |
570 | g_assert (!g_task_had_error (G_TASK (result))); |
571 | g_assert_false (g_task_get_completed (G_TASK (result))); |
572 | |
573 | g_task_propagate_boolean (G_TASK (result), error: &error); |
574 | g_assert_no_error (error); |
575 | |
576 | g_assert (!g_task_had_error (G_TASK (result))); |
577 | |
578 | *ret_out = ++counter; |
579 | |
580 | if (counter == 3) |
581 | g_main_loop_quit (loop); |
582 | } |
583 | |
584 | static void |
585 | test_priority (void) |
586 | { |
587 | GTask *t1, *t2, *t3; |
588 | gssize ret1, ret2, ret3; |
589 | |
590 | /* t2 has higher priority than either t1 or t3, so we can't |
591 | * accidentally pass the test just by completing the tasks in the |
592 | * order they were created (or in reverse order). |
593 | */ |
594 | |
595 | t1 = g_task_new (NULL, NULL, callback: priority_callback, callback_data: &ret1); |
596 | g_task_set_priority (task: t1, G_PRIORITY_DEFAULT); |
597 | g_task_return_boolean (task: t1, TRUE); |
598 | g_object_unref (object: t1); |
599 | |
600 | t2 = g_task_new (NULL, NULL, callback: priority_callback, callback_data: &ret2); |
601 | g_task_set_priority (task: t2, G_PRIORITY_HIGH); |
602 | g_task_return_boolean (task: t2, TRUE); |
603 | g_object_unref (object: t2); |
604 | |
605 | t3 = g_task_new (NULL, NULL, callback: priority_callback, callback_data: &ret3); |
606 | g_task_set_priority (task: t3, G_PRIORITY_LOW); |
607 | g_task_return_boolean (task: t3, TRUE); |
608 | g_object_unref (object: t3); |
609 | |
610 | g_main_loop_run (loop); |
611 | |
612 | g_assert_cmpint (ret2, ==, 1); |
613 | g_assert_cmpint (ret1, ==, 2); |
614 | g_assert_cmpint (ret3, ==, 3); |
615 | } |
616 | |
617 | /* Test that getting and setting the task name works. */ |
618 | static void name_callback (GObject *object, |
619 | GAsyncResult *result, |
620 | gpointer user_data); |
621 | |
622 | static void |
623 | test_name (void) |
624 | { |
625 | GTask *t1 = NULL; |
626 | gchar *name1 = NULL; |
627 | |
628 | t1 = g_task_new (NULL, NULL, callback: name_callback, callback_data: &name1); |
629 | g_task_set_name (task: t1, name: "some task" ); |
630 | g_task_return_boolean (task: t1, TRUE); |
631 | g_object_unref (object: t1); |
632 | |
633 | g_main_loop_run (loop); |
634 | |
635 | g_assert_cmpstr (name1, ==, "some task" ); |
636 | |
637 | g_free (mem: name1); |
638 | } |
639 | |
640 | static void |
641 | name_callback (GObject *object, |
642 | GAsyncResult *result, |
643 | gpointer user_data) |
644 | { |
645 | gchar **name_out = user_data; |
646 | GError *local_error = NULL; |
647 | |
648 | g_assert_null (*name_out); |
649 | *name_out = g_strdup (str: g_task_get_name (G_TASK (result))); |
650 | |
651 | g_task_propagate_boolean (G_TASK (result), error: &local_error); |
652 | g_assert_no_error (local_error); |
653 | |
654 | g_main_loop_quit (loop); |
655 | } |
656 | |
657 | /* test_asynchronous_cancellation: cancelled tasks are returned |
658 | * asynchronously, i.e. not from inside the GCancellable::cancelled |
659 | * handler. |
660 | * |
661 | * The test is set up further below in test_asynchronous_cancellation. |
662 | */ |
663 | |
664 | /* asynchronous_cancellation_callback represents the callback that the |
665 | * caller of a typical asynchronous API would have passed. See |
666 | * test_asynchronous_cancellation. |
667 | */ |
668 | static void |
669 | asynchronous_cancellation_callback (GObject *object, |
670 | GAsyncResult *result, |
671 | gpointer user_data) |
672 | { |
673 | GError *error = NULL; |
674 | guint run_task_id; |
675 | |
676 | g_assert_null (object); |
677 | g_assert_true (g_task_is_valid (result, object)); |
678 | g_assert_true (g_async_result_get_user_data (result) == user_data); |
679 | g_assert_true (g_task_had_error (G_TASK (result))); |
680 | g_assert_false (g_task_get_completed (G_TASK (result))); |
681 | |
682 | run_task_id = GPOINTER_TO_UINT (g_task_get_task_data (G_TASK (result))); |
683 | g_assert_cmpuint (run_task_id, ==, 0); |
684 | |
685 | g_task_propagate_boolean (G_TASK (result), error: &error); |
686 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
687 | g_clear_error (err: &error); |
688 | |
689 | g_assert_true (g_task_had_error (G_TASK (result))); |
690 | |
691 | g_main_loop_quit (loop); |
692 | } |
693 | |
694 | /* asynchronous_cancellation_cancel_task represents a user cancelling |
695 | * the ongoing operation. To make it somewhat realistic it is delayed |
696 | * by 50ms via a timeout GSource. See test_asynchronous_cancellation. |
697 | */ |
698 | static gboolean |
699 | asynchronous_cancellation_cancel_task (gpointer user_data) |
700 | { |
701 | GCancellable *cancellable; |
702 | GTask *task = G_TASK (user_data); |
703 | |
704 | cancellable = g_task_get_cancellable (task); |
705 | g_assert_true (G_IS_CANCELLABLE (cancellable)); |
706 | |
707 | g_cancellable_cancel (cancellable); |
708 | g_assert_false (g_task_get_completed (task)); |
709 | |
710 | return G_SOURCE_REMOVE; |
711 | } |
712 | |
713 | /* asynchronous_cancellation_cancelled is the GCancellable::cancelled |
714 | * handler that's used by the asynchronous implementation for |
715 | * cancelling itself. |
716 | */ |
717 | static void |
718 | asynchronous_cancellation_cancelled (GCancellable *cancellable, |
719 | gpointer user_data) |
720 | { |
721 | GTask *task = G_TASK (user_data); |
722 | guint run_task_id; |
723 | |
724 | g_assert_true (cancellable == g_task_get_cancellable (task)); |
725 | |
726 | run_task_id = GPOINTER_TO_UINT (g_task_get_task_data (task)); |
727 | g_assert_cmpuint (run_task_id, !=, 0); |
728 | |
729 | g_source_remove (tag: run_task_id); |
730 | g_task_set_task_data (task, GUINT_TO_POINTER (0), NULL); |
731 | |
732 | g_task_return_boolean (task, FALSE); |
733 | g_assert_false (g_task_get_completed (task)); |
734 | } |
735 | |
736 | /* asynchronous_cancellation_run_task represents the actual |
737 | * asynchronous work being done in an idle GSource as was mentioned |
738 | * above. This is effectively meant to be an infinite loop so that |
739 | * the only way to break out of it is via cancellation. |
740 | */ |
741 | static gboolean |
742 | asynchronous_cancellation_run_task (gpointer user_data) |
743 | { |
744 | GCancellable *cancellable; |
745 | GTask *task = G_TASK (user_data); |
746 | |
747 | cancellable = g_task_get_cancellable (task); |
748 | g_assert_true (G_IS_CANCELLABLE (cancellable)); |
749 | g_assert_false (g_cancellable_is_cancelled (cancellable)); |
750 | |
751 | return G_SOURCE_CONTINUE; |
752 | } |
753 | |
754 | /* Test that cancellation is always asynchronous. The completion callback for |
755 | * a #GTask must not be called from inside the cancellation handler. |
756 | * |
757 | * The body of the loop inside test_asynchronous_cancellation |
758 | * represents what would have been a typical asynchronous API call, |
759 | * and its implementation. They are fused together without an API |
760 | * boundary. The actual work done by this asynchronous API is |
761 | * represented by an idle GSource. |
762 | */ |
763 | static void |
764 | test_asynchronous_cancellation (void) |
765 | { |
766 | guint i; |
767 | |
768 | g_test_bug (bug_uri_snippet: "https://gitlab.gnome.org/GNOME/glib/issues/1608" ); |
769 | |
770 | /* Run a few times to shake out any timing issues between the |
771 | * cancellation and task sources. |
772 | */ |
773 | for (i = 0; i < 5; i++) |
774 | { |
775 | GCancellable *cancellable; |
776 | GTask *task; |
777 | gboolean notification_emitted = FALSE; |
778 | guint run_task_id; |
779 | |
780 | cancellable = g_cancellable_new (); |
781 | |
782 | task = g_task_new (NULL, cancellable, callback: asynchronous_cancellation_callback, NULL); |
783 | g_cancellable_connect (cancellable, callback: (GCallback) asynchronous_cancellation_cancelled, data: task, NULL); |
784 | g_signal_connect (task, "notify::completed" , (GCallback) completed_cb, ¬ification_emitted); |
785 | |
786 | run_task_id = g_idle_add (function: asynchronous_cancellation_run_task, data: task); |
787 | g_source_set_name_by_id (tag: run_task_id, name: "[test_asynchronous_cancellation] run_task" ); |
788 | g_task_set_task_data (task, GUINT_TO_POINTER (run_task_id), NULL); |
789 | |
790 | g_timeout_add (interval: 50, function: asynchronous_cancellation_cancel_task, data: task); |
791 | |
792 | g_main_loop_run (loop); |
793 | |
794 | g_assert_true (g_task_get_completed (task)); |
795 | g_assert_true (notification_emitted); |
796 | |
797 | g_object_unref (object: cancellable); |
798 | g_object_unref (object: task); |
799 | } |
800 | } |
801 | |
802 | /* test_check_cancellable: cancellation overrides return value */ |
803 | |
804 | enum { |
805 | CANCEL_BEFORE = (1 << 1), |
806 | CANCEL_AFTER = (1 << 2), |
807 | CHECK_CANCELLABLE = (1 << 3) |
808 | }; |
809 | #define NUM_CANCEL_TESTS (CANCEL_BEFORE | CANCEL_AFTER | CHECK_CANCELLABLE) |
810 | |
811 | static void |
812 | cancel_callback (GObject *object, |
813 | GAsyncResult *result, |
814 | gpointer user_data) |
815 | { |
816 | int state = GPOINTER_TO_INT (user_data); |
817 | GTask *task; |
818 | GCancellable *cancellable; |
819 | GError *error = NULL; |
820 | |
821 | g_assert (object == NULL); |
822 | g_assert (g_task_is_valid (result, object)); |
823 | g_assert (g_async_result_get_user_data (result) == user_data); |
824 | |
825 | task = G_TASK (result); |
826 | cancellable = g_task_get_cancellable (task); |
827 | g_assert (G_IS_CANCELLABLE (cancellable)); |
828 | |
829 | if (state & (CANCEL_BEFORE | CANCEL_AFTER)) |
830 | g_assert (g_cancellable_is_cancelled (cancellable)); |
831 | else |
832 | g_assert (!g_cancellable_is_cancelled (cancellable)); |
833 | |
834 | if (state & CHECK_CANCELLABLE) |
835 | g_assert (g_task_get_check_cancellable (task)); |
836 | else |
837 | g_assert (!g_task_get_check_cancellable (task)); |
838 | |
839 | if (g_task_propagate_boolean (task, error: &error)) |
840 | { |
841 | g_assert (!g_cancellable_is_cancelled (cancellable) || |
842 | !g_task_get_check_cancellable (task)); |
843 | } |
844 | else |
845 | { |
846 | g_assert (g_cancellable_is_cancelled (cancellable) && |
847 | g_task_get_check_cancellable (task)); |
848 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
849 | g_error_free (error); |
850 | } |
851 | |
852 | g_main_loop_quit (loop); |
853 | } |
854 | |
855 | static void |
856 | test_check_cancellable (void) |
857 | { |
858 | GTask *task; |
859 | GCancellable *cancellable; |
860 | int state; |
861 | |
862 | cancellable = g_cancellable_new (); |
863 | |
864 | for (state = 0; state <= NUM_CANCEL_TESTS; state++) |
865 | { |
866 | task = g_task_new (NULL, cancellable, callback: cancel_callback, |
867 | GINT_TO_POINTER (state)); |
868 | g_task_set_check_cancellable (task, check_cancellable: (state & CHECK_CANCELLABLE) != 0); |
869 | |
870 | if (state & CANCEL_BEFORE) |
871 | g_cancellable_cancel (cancellable); |
872 | g_task_return_boolean (task, TRUE); |
873 | if (state & CANCEL_AFTER) |
874 | g_cancellable_cancel (cancellable); |
875 | |
876 | g_main_loop_run (loop); |
877 | g_object_unref (object: task); |
878 | g_cancellable_reset (cancellable); |
879 | } |
880 | |
881 | g_object_unref (object: cancellable); |
882 | } |
883 | |
884 | /* test_return_if_cancelled */ |
885 | |
886 | static void |
887 | return_if_cancelled_callback (GObject *object, |
888 | GAsyncResult *result, |
889 | gpointer user_data) |
890 | { |
891 | GError *error = NULL; |
892 | |
893 | g_assert (object == NULL); |
894 | g_assert (g_task_is_valid (result, object)); |
895 | g_assert (g_async_result_get_user_data (result) == user_data); |
896 | g_assert (g_task_had_error (G_TASK (result))); |
897 | g_assert_false (g_task_get_completed (G_TASK (result))); |
898 | |
899 | g_task_propagate_boolean (G_TASK (result), error: &error); |
900 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
901 | g_clear_error (err: &error); |
902 | |
903 | g_assert (g_task_had_error (G_TASK (result))); |
904 | |
905 | g_main_loop_quit (loop); |
906 | } |
907 | |
908 | static void |
909 | test_return_if_cancelled (void) |
910 | { |
911 | GTask *task; |
912 | GCancellable *cancellable; |
913 | gboolean cancelled; |
914 | gboolean notification_emitted = FALSE; |
915 | |
916 | cancellable = g_cancellable_new (); |
917 | |
918 | task = g_task_new (NULL, cancellable, callback: return_if_cancelled_callback, NULL); |
919 | g_signal_connect (task, "notify::completed" , |
920 | (GCallback) completed_cb, ¬ification_emitted); |
921 | |
922 | g_cancellable_cancel (cancellable); |
923 | cancelled = g_task_return_error_if_cancelled (task); |
924 | g_assert (cancelled); |
925 | g_assert_false (notification_emitted); |
926 | g_main_loop_run (loop); |
927 | g_object_unref (object: task); |
928 | g_assert_true (notification_emitted); |
929 | g_cancellable_reset (cancellable); |
930 | |
931 | notification_emitted = FALSE; |
932 | |
933 | task = g_task_new (NULL, cancellable, callback: return_if_cancelled_callback, NULL); |
934 | g_signal_connect (task, "notify::completed" , |
935 | (GCallback) completed_cb, ¬ification_emitted); |
936 | |
937 | g_task_set_check_cancellable (task, FALSE); |
938 | g_cancellable_cancel (cancellable); |
939 | cancelled = g_task_return_error_if_cancelled (task); |
940 | g_assert (cancelled); |
941 | g_assert_false (notification_emitted); |
942 | g_main_loop_run (loop); |
943 | g_object_unref (object: task); |
944 | g_assert_true (notification_emitted); |
945 | g_object_unref (object: cancellable); |
946 | } |
947 | |
948 | /* test_run_in_thread */ |
949 | |
950 | static GMutex run_in_thread_mutex; |
951 | static GCond run_in_thread_cond; |
952 | |
953 | static void |
954 | task_weak_notify (gpointer user_data, |
955 | GObject *ex_task) |
956 | { |
957 | gboolean *weak_notify_ran = user_data; |
958 | |
959 | g_mutex_lock (mutex: &run_in_thread_mutex); |
960 | g_atomic_int_set (weak_notify_ran, TRUE); |
961 | g_cond_signal (cond: &run_in_thread_cond); |
962 | g_mutex_unlock (mutex: &run_in_thread_mutex); |
963 | } |
964 | |
965 | static void |
966 | run_in_thread_callback (GObject *object, |
967 | GAsyncResult *result, |
968 | gpointer user_data) |
969 | { |
970 | gboolean *done = user_data; |
971 | GError *error = NULL; |
972 | gssize ret; |
973 | |
974 | g_assert (g_thread_self () == main_thread); |
975 | |
976 | g_assert (object == NULL); |
977 | g_assert (g_task_is_valid (result, object)); |
978 | g_assert (g_async_result_get_user_data (result) == user_data); |
979 | g_assert (!g_task_had_error (G_TASK (result))); |
980 | g_assert_false (g_task_get_completed (G_TASK (result))); |
981 | g_assert_cmpstr (g_task_get_name (G_TASK (result)), ==, "test_run_in_thread name" ); |
982 | |
983 | ret = g_task_propagate_int (G_TASK (result), error: &error); |
984 | g_assert_no_error (error); |
985 | g_assert_cmpint (ret, ==, magic); |
986 | |
987 | g_assert (!g_task_had_error (G_TASK (result))); |
988 | |
989 | *done = TRUE; |
990 | g_main_loop_quit (loop); |
991 | } |
992 | |
993 | static void |
994 | run_in_thread_thread (GTask *task, |
995 | gpointer source_object, |
996 | gpointer task_data, |
997 | GCancellable *cancellable) |
998 | { |
999 | gboolean *thread_ran = task_data; |
1000 | |
1001 | g_assert (source_object == g_task_get_source_object (task)); |
1002 | g_assert (task_data == g_task_get_task_data (task)); |
1003 | g_assert (cancellable == g_task_get_cancellable (task)); |
1004 | g_assert_false (g_task_get_completed (task)); |
1005 | g_assert_cmpstr (g_task_get_name (task), ==, "test_run_in_thread name" ); |
1006 | |
1007 | g_assert (g_thread_self () != main_thread); |
1008 | |
1009 | g_mutex_lock (mutex: &run_in_thread_mutex); |
1010 | g_atomic_int_set (thread_ran, TRUE); |
1011 | g_cond_signal (cond: &run_in_thread_cond); |
1012 | g_mutex_unlock (mutex: &run_in_thread_mutex); |
1013 | |
1014 | g_task_return_int (task, result: magic); |
1015 | } |
1016 | |
1017 | static void |
1018 | test_run_in_thread (void) |
1019 | { |
1020 | GTask *task; |
1021 | gboolean thread_ran = FALSE; /* (atomic) */ |
1022 | gboolean weak_notify_ran = FALSE; /* (atomic) */ |
1023 | gboolean notification_emitted = FALSE; |
1024 | gboolean done = FALSE; |
1025 | |
1026 | task = g_task_new (NULL, NULL, callback: run_in_thread_callback, callback_data: &done); |
1027 | g_task_set_name (task, name: "test_run_in_thread name" ); |
1028 | g_object_weak_ref (G_OBJECT (task), notify: task_weak_notify, data: (gpointer)&weak_notify_ran); |
1029 | g_signal_connect (task, "notify::completed" , |
1030 | (GCallback) completed_cb, ¬ification_emitted); |
1031 | |
1032 | g_task_set_task_data (task, task_data: (gpointer)&thread_ran, NULL); |
1033 | g_task_run_in_thread (task, task_func: run_in_thread_thread); |
1034 | |
1035 | g_mutex_lock (mutex: &run_in_thread_mutex); |
1036 | while (!g_atomic_int_get (&thread_ran)) |
1037 | g_cond_wait (cond: &run_in_thread_cond, mutex: &run_in_thread_mutex); |
1038 | g_mutex_unlock (mutex: &run_in_thread_mutex); |
1039 | |
1040 | g_assert (done == FALSE); |
1041 | g_assert_false (g_atomic_int_get (&weak_notify_ran)); |
1042 | |
1043 | g_main_loop_run (loop); |
1044 | |
1045 | g_assert (done == TRUE); |
1046 | g_assert_true (notification_emitted); |
1047 | |
1048 | g_assert_cmpstr (g_task_get_name (task), ==, "test_run_in_thread name" ); |
1049 | |
1050 | g_object_unref (object: task); |
1051 | |
1052 | g_mutex_lock (mutex: &run_in_thread_mutex); |
1053 | while (!g_atomic_int_get (&weak_notify_ran)) |
1054 | g_cond_wait (cond: &run_in_thread_cond, mutex: &run_in_thread_mutex); |
1055 | g_mutex_unlock (mutex: &run_in_thread_mutex); |
1056 | } |
1057 | |
1058 | /* test_run_in_thread_sync */ |
1059 | |
1060 | static void |
1061 | run_in_thread_sync_callback (GObject *object, |
1062 | GAsyncResult *result, |
1063 | gpointer user_data) |
1064 | { |
1065 | /* g_task_run_in_thread_sync() does not invoke the task's callback */ |
1066 | g_assert_not_reached (); |
1067 | } |
1068 | |
1069 | static void |
1070 | run_in_thread_sync_thread (GTask *task, |
1071 | gpointer source_object, |
1072 | gpointer task_data, |
1073 | GCancellable *cancellable) |
1074 | { |
1075 | gboolean *thread_ran = task_data; |
1076 | |
1077 | g_assert (source_object == g_task_get_source_object (task)); |
1078 | g_assert (task_data == g_task_get_task_data (task)); |
1079 | g_assert (cancellable == g_task_get_cancellable (task)); |
1080 | g_assert_false (g_task_get_completed (task)); |
1081 | |
1082 | g_assert (g_thread_self () != main_thread); |
1083 | |
1084 | g_atomic_int_set (thread_ran, TRUE); |
1085 | g_task_return_int (task, result: magic); |
1086 | } |
1087 | |
1088 | static void |
1089 | test_run_in_thread_sync (void) |
1090 | { |
1091 | GTask *task; |
1092 | gboolean thread_ran = FALSE; |
1093 | gssize ret; |
1094 | gboolean notification_emitted = FALSE; |
1095 | GError *error = NULL; |
1096 | |
1097 | task = g_task_new (NULL, NULL, callback: run_in_thread_sync_callback, NULL); |
1098 | g_signal_connect (task, "notify::completed" , |
1099 | (GCallback) completed_cb, |
1100 | ¬ification_emitted); |
1101 | |
1102 | g_task_set_task_data (task, task_data: &thread_ran, NULL); |
1103 | g_task_run_in_thread_sync (task, task_func: run_in_thread_sync_thread); |
1104 | |
1105 | g_assert_true (g_atomic_int_get (&thread_ran)); |
1106 | g_assert (task != NULL); |
1107 | g_assert (!g_task_had_error (task)); |
1108 | g_assert_true (g_task_get_completed (task)); |
1109 | g_assert_true (notification_emitted); |
1110 | |
1111 | ret = g_task_propagate_int (task, error: &error); |
1112 | g_assert_no_error (error); |
1113 | g_assert_cmpint (ret, ==, magic); |
1114 | |
1115 | g_assert (!g_task_had_error (task)); |
1116 | |
1117 | g_object_unref (object: task); |
1118 | } |
1119 | |
1120 | /* test_run_in_thread_priority */ |
1121 | |
1122 | static GMutex fake_task_mutex, last_fake_task_mutex; |
1123 | static gint sequence_number = 0; |
1124 | |
1125 | static void |
1126 | quit_main_loop_callback (GObject *object, |
1127 | GAsyncResult *result, |
1128 | gpointer user_data) |
1129 | { |
1130 | GError *error = NULL; |
1131 | gboolean ret; |
1132 | |
1133 | g_assert (g_thread_self () == main_thread); |
1134 | |
1135 | g_assert (object == NULL); |
1136 | g_assert (g_task_is_valid (result, object)); |
1137 | g_assert (g_async_result_get_user_data (result) == user_data); |
1138 | g_assert (!g_task_had_error (G_TASK (result))); |
1139 | g_assert_false (g_task_get_completed (G_TASK (result))); |
1140 | |
1141 | ret = g_task_propagate_boolean (G_TASK (result), error: &error); |
1142 | g_assert_no_error (error); |
1143 | g_assert_cmpint (ret, ==, TRUE); |
1144 | |
1145 | g_assert (!g_task_had_error (G_TASK (result))); |
1146 | |
1147 | g_main_loop_quit (loop); |
1148 | } |
1149 | |
1150 | static void |
1151 | set_sequence_number_thread (GTask *task, |
1152 | gpointer source_object, |
1153 | gpointer task_data, |
1154 | GCancellable *cancellable) |
1155 | { |
1156 | gint *seq_no_p = task_data; |
1157 | |
1158 | *seq_no_p = ++sequence_number; |
1159 | g_task_return_boolean (task, TRUE); |
1160 | } |
1161 | |
1162 | static void |
1163 | fake_task_thread (GTask *task, |
1164 | gpointer source_object, |
1165 | gpointer task_data, |
1166 | GCancellable *cancellable) |
1167 | { |
1168 | GMutex *mutex = task_data; |
1169 | |
1170 | g_mutex_lock (mutex); |
1171 | g_mutex_unlock (mutex); |
1172 | g_task_return_boolean (task, TRUE); |
1173 | } |
1174 | |
1175 | #define G_TASK_THREAD_POOL_SIZE 10 |
1176 | static int fake_tasks_running; |
1177 | |
1178 | static void |
1179 | fake_task_callback (GObject *source, |
1180 | GAsyncResult *result, |
1181 | gpointer user_data) |
1182 | { |
1183 | if (--fake_tasks_running == 0) |
1184 | g_main_loop_quit (loop); |
1185 | } |
1186 | |
1187 | static void |
1188 | clog_up_thread_pool (void) |
1189 | { |
1190 | GTask *task; |
1191 | int i; |
1192 | |
1193 | g_thread_pool_stop_unused_threads (); |
1194 | |
1195 | g_mutex_lock (mutex: &fake_task_mutex); |
1196 | for (i = 0; i < G_TASK_THREAD_POOL_SIZE - 1; i++) |
1197 | { |
1198 | task = g_task_new (NULL, NULL, callback: fake_task_callback, NULL); |
1199 | g_task_set_task_data (task, task_data: &fake_task_mutex, NULL); |
1200 | g_assert_cmpint (g_task_get_priority (task), ==, G_PRIORITY_DEFAULT); |
1201 | g_task_set_priority (task, G_PRIORITY_HIGH * 2); |
1202 | g_assert_cmpint (g_task_get_priority (task), ==, G_PRIORITY_HIGH * 2); |
1203 | g_task_run_in_thread (task, task_func: fake_task_thread); |
1204 | g_object_unref (object: task); |
1205 | fake_tasks_running++; |
1206 | } |
1207 | |
1208 | g_mutex_lock (mutex: &last_fake_task_mutex); |
1209 | task = g_task_new (NULL, NULL, NULL, NULL); |
1210 | g_task_set_task_data (task, task_data: &last_fake_task_mutex, NULL); |
1211 | g_task_set_priority (task, G_PRIORITY_HIGH * 2); |
1212 | g_task_run_in_thread (task, task_func: fake_task_thread); |
1213 | g_object_unref (object: task); |
1214 | } |
1215 | |
1216 | static void |
1217 | unclog_thread_pool (void) |
1218 | { |
1219 | g_mutex_unlock (mutex: &fake_task_mutex); |
1220 | g_main_loop_run (loop); |
1221 | } |
1222 | |
1223 | static void |
1224 | test_run_in_thread_priority (void) |
1225 | { |
1226 | GTask *task; |
1227 | GCancellable *cancellable; |
1228 | int seq_a, seq_b, seq_c, seq_d; |
1229 | |
1230 | clog_up_thread_pool (); |
1231 | |
1232 | /* Queue three more tasks that we'll arrange to have run serially */ |
1233 | task = g_task_new (NULL, NULL, NULL, NULL); |
1234 | g_task_set_task_data (task, task_data: &seq_a, NULL); |
1235 | g_task_run_in_thread (task, task_func: set_sequence_number_thread); |
1236 | g_object_unref (object: task); |
1237 | |
1238 | task = g_task_new (NULL, NULL, callback: quit_main_loop_callback, NULL); |
1239 | g_task_set_task_data (task, task_data: &seq_b, NULL); |
1240 | g_task_set_priority (task, G_PRIORITY_LOW); |
1241 | g_task_run_in_thread (task, task_func: set_sequence_number_thread); |
1242 | g_object_unref (object: task); |
1243 | |
1244 | task = g_task_new (NULL, NULL, NULL, NULL); |
1245 | g_task_set_task_data (task, task_data: &seq_c, NULL); |
1246 | g_task_set_priority (task, G_PRIORITY_HIGH); |
1247 | g_task_run_in_thread (task, task_func: set_sequence_number_thread); |
1248 | g_object_unref (object: task); |
1249 | |
1250 | cancellable = g_cancellable_new (); |
1251 | task = g_task_new (NULL, cancellable, NULL, NULL); |
1252 | g_task_set_task_data (task, task_data: &seq_d, NULL); |
1253 | g_task_run_in_thread (task, task_func: set_sequence_number_thread); |
1254 | g_cancellable_cancel (cancellable); |
1255 | g_object_unref (object: cancellable); |
1256 | g_object_unref (object: task); |
1257 | |
1258 | /* Let the last fake task complete; the four other tasks will then |
1259 | * complete serially, in the order D, C, A, B, and B will quit the |
1260 | * main loop. |
1261 | */ |
1262 | g_mutex_unlock (mutex: &last_fake_task_mutex); |
1263 | g_main_loop_run (loop); |
1264 | |
1265 | g_assert_cmpint (seq_d, ==, 1); |
1266 | g_assert_cmpint (seq_c, ==, 2); |
1267 | g_assert_cmpint (seq_a, ==, 3); |
1268 | g_assert_cmpint (seq_b, ==, 4); |
1269 | |
1270 | unclog_thread_pool (); |
1271 | } |
1272 | |
1273 | /* test_run_in_thread_nested: task threads that block waiting on |
1274 | * other task threads will not cause the thread pool to starve. |
1275 | */ |
1276 | |
1277 | static void |
1278 | run_nested_task_thread (GTask *task, |
1279 | gpointer source_object, |
1280 | gpointer task_data, |
1281 | GCancellable *cancellable) |
1282 | { |
1283 | GTask *nested; |
1284 | int *nested_tasks_left = task_data; |
1285 | |
1286 | if ((*nested_tasks_left)--) |
1287 | { |
1288 | nested = g_task_new (NULL, NULL, NULL, NULL); |
1289 | g_task_set_task_data (task: nested, task_data: nested_tasks_left, NULL); |
1290 | g_task_run_in_thread_sync (task: nested, task_func: run_nested_task_thread); |
1291 | g_object_unref (object: nested); |
1292 | } |
1293 | |
1294 | g_task_return_boolean (task, TRUE); |
1295 | } |
1296 | |
1297 | static void |
1298 | test_run_in_thread_nested (void) |
1299 | { |
1300 | GTask *task; |
1301 | int nested_tasks_left = 2; |
1302 | |
1303 | clog_up_thread_pool (); |
1304 | |
1305 | task = g_task_new (NULL, NULL, callback: quit_main_loop_callback, NULL); |
1306 | g_task_set_task_data (task, task_data: &nested_tasks_left, NULL); |
1307 | g_task_run_in_thread (task, task_func: run_nested_task_thread); |
1308 | g_object_unref (object: task); |
1309 | |
1310 | g_mutex_unlock (mutex: &last_fake_task_mutex); |
1311 | g_main_loop_run (loop); |
1312 | |
1313 | unclog_thread_pool (); |
1314 | } |
1315 | |
1316 | /* test_run_in_thread_overflow: if you queue lots and lots and lots of |
1317 | * tasks, they won't all run at once. |
1318 | */ |
1319 | static GMutex overflow_mutex; |
1320 | static guint overflow_completed; |
1321 | |
1322 | static void |
1323 | run_overflow_task_thread (GTask *task, |
1324 | gpointer source_object, |
1325 | gpointer task_data, |
1326 | GCancellable *cancellable) |
1327 | { |
1328 | gchar *result = task_data; |
1329 | |
1330 | if (g_task_return_error_if_cancelled (task)) |
1331 | { |
1332 | *result = 'X'; |
1333 | } |
1334 | else |
1335 | { |
1336 | /* Block until the main thread is ready. */ |
1337 | g_mutex_lock (mutex: &overflow_mutex); |
1338 | g_mutex_unlock (mutex: &overflow_mutex); |
1339 | |
1340 | *result = '.'; |
1341 | |
1342 | g_task_return_boolean (task, TRUE); |
1343 | } |
1344 | |
1345 | g_atomic_int_inc (&overflow_completed); |
1346 | } |
1347 | |
1348 | #define NUM_OVERFLOW_TASKS 1024 |
1349 | |
1350 | static void |
1351 | test_run_in_thread_overflow (void) |
1352 | { |
1353 | GCancellable *cancellable; |
1354 | GTask *task; |
1355 | gchar buf[NUM_OVERFLOW_TASKS + 1]; |
1356 | gint i; |
1357 | |
1358 | /* Queue way too many tasks and then sleep for a bit. The first 10 |
1359 | * tasks will be dispatched to threads and will then block on |
1360 | * overflow_mutex, so more threads will be created while this thread |
1361 | * is sleeping. Then we cancel the cancellable, unlock the mutex, |
1362 | * wait for all of the tasks to complete, and make sure that we got |
1363 | * the behavior we expected. |
1364 | */ |
1365 | |
1366 | memset (s: buf, c: 0, n: sizeof (buf)); |
1367 | cancellable = g_cancellable_new (); |
1368 | |
1369 | g_mutex_lock (mutex: &overflow_mutex); |
1370 | |
1371 | for (i = 0; i < NUM_OVERFLOW_TASKS; i++) |
1372 | { |
1373 | task = g_task_new (NULL, cancellable, NULL, NULL); |
1374 | g_task_set_task_data (task, task_data: buf + i, NULL); |
1375 | g_task_run_in_thread (task, task_func: run_overflow_task_thread); |
1376 | g_object_unref (object: task); |
1377 | } |
1378 | |
1379 | if (g_test_slow ()) |
1380 | g_usleep (microseconds: 5000000); /* 5 s */ |
1381 | else |
1382 | g_usleep (microseconds: 500000); /* 0.5 s */ |
1383 | g_cancellable_cancel (cancellable); |
1384 | g_object_unref (object: cancellable); |
1385 | |
1386 | g_mutex_unlock (mutex: &overflow_mutex); |
1387 | |
1388 | /* Wait for all tasks to complete. */ |
1389 | while (g_atomic_int_get (&overflow_completed) != NUM_OVERFLOW_TASKS) |
1390 | g_usleep (microseconds: 1000); |
1391 | |
1392 | g_assert_cmpint (strlen (buf), ==, NUM_OVERFLOW_TASKS); |
1393 | |
1394 | i = strspn (s: buf, accept: "." ); |
1395 | /* Given the sleep times above, i should be 14 for normal, 40 for |
1396 | * slow. But if the machine is too slow/busy then the scheduling |
1397 | * might get messed up and we'll get more or fewer threads than |
1398 | * expected. But there are limits to how messed up it could |
1399 | * plausibly get (and we hope that if gtask is actually broken then |
1400 | * it will exceed those limits). |
1401 | */ |
1402 | g_assert_cmpint (i, >=, 10); |
1403 | if (g_test_slow ()) |
1404 | g_assert_cmpint (i, <, 50); |
1405 | else |
1406 | g_assert_cmpint (i, <, 20); |
1407 | |
1408 | g_assert_cmpint (i + strspn (buf + i, "X" ), ==, NUM_OVERFLOW_TASKS); |
1409 | } |
1410 | |
1411 | /* test_return_on_cancel */ |
1412 | |
1413 | GMutex roc_init_mutex, roc_finish_mutex; |
1414 | GCond roc_init_cond, roc_finish_cond; |
1415 | |
1416 | typedef enum { |
1417 | THREAD_STARTING, |
1418 | THREAD_RUNNING, |
1419 | THREAD_CANCELLED, |
1420 | THREAD_COMPLETED |
1421 | } ThreadState; |
1422 | |
1423 | static void |
1424 | return_on_cancel_callback (GObject *object, |
1425 | GAsyncResult *result, |
1426 | gpointer user_data) |
1427 | { |
1428 | gboolean *callback_ran = user_data; |
1429 | GError *error = NULL; |
1430 | gssize ret; |
1431 | |
1432 | g_assert (g_thread_self () == main_thread); |
1433 | |
1434 | g_assert (object == NULL); |
1435 | g_assert (g_task_is_valid (result, object)); |
1436 | g_assert (g_async_result_get_user_data (result) == user_data); |
1437 | g_assert (g_task_had_error (G_TASK (result))); |
1438 | g_assert_false (g_task_get_completed (G_TASK (result))); |
1439 | |
1440 | ret = g_task_propagate_int (G_TASK (result), error: &error); |
1441 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1442 | g_clear_error (err: &error); |
1443 | g_assert_cmpint (ret, ==, -1); |
1444 | |
1445 | g_assert (g_task_had_error (G_TASK (result))); |
1446 | |
1447 | *callback_ran = TRUE; |
1448 | g_main_loop_quit (loop); |
1449 | } |
1450 | |
1451 | static void |
1452 | return_on_cancel_thread (GTask *task, |
1453 | gpointer source_object, |
1454 | gpointer task_data, |
1455 | GCancellable *cancellable) |
1456 | { |
1457 | ThreadState *state = task_data; |
1458 | |
1459 | g_assert (source_object == g_task_get_source_object (task)); |
1460 | g_assert (task_data == g_task_get_task_data (task)); |
1461 | g_assert (cancellable == g_task_get_cancellable (task)); |
1462 | |
1463 | g_assert (g_thread_self () != main_thread); |
1464 | |
1465 | g_mutex_lock (mutex: &roc_init_mutex); |
1466 | *state = THREAD_RUNNING; |
1467 | g_cond_signal (cond: &roc_init_cond); |
1468 | g_mutex_unlock (mutex: &roc_init_mutex); |
1469 | |
1470 | g_mutex_lock (mutex: &roc_finish_mutex); |
1471 | |
1472 | if (!g_task_get_return_on_cancel (task) || |
1473 | g_task_set_return_on_cancel (task, FALSE)) |
1474 | { |
1475 | *state = THREAD_COMPLETED; |
1476 | g_task_return_int (task, result: magic); |
1477 | } |
1478 | else |
1479 | *state = THREAD_CANCELLED; |
1480 | |
1481 | g_cond_signal (cond: &roc_finish_cond); |
1482 | g_mutex_unlock (mutex: &roc_finish_mutex); |
1483 | } |
1484 | |
1485 | static void |
1486 | test_return_on_cancel (void) |
1487 | { |
1488 | GTask *task; |
1489 | GCancellable *cancellable; |
1490 | ThreadState thread_state; /* (atomic) */ |
1491 | gboolean weak_notify_ran = FALSE; /* (atomic) */ |
1492 | gboolean callback_ran; |
1493 | gboolean notification_emitted = FALSE; |
1494 | |
1495 | cancellable = g_cancellable_new (); |
1496 | |
1497 | /* If return-on-cancel is FALSE (default), the task does not return |
1498 | * early. |
1499 | */ |
1500 | callback_ran = FALSE; |
1501 | g_atomic_int_set (&thread_state, THREAD_STARTING); |
1502 | task = g_task_new (NULL, cancellable, callback: return_on_cancel_callback, callback_data: &callback_ran); |
1503 | g_signal_connect (task, "notify::completed" , |
1504 | (GCallback) completed_cb, ¬ification_emitted); |
1505 | |
1506 | g_task_set_task_data (task, task_data: (gpointer)&thread_state, NULL); |
1507 | g_mutex_lock (mutex: &roc_init_mutex); |
1508 | g_mutex_lock (mutex: &roc_finish_mutex); |
1509 | g_task_run_in_thread (task, task_func: return_on_cancel_thread); |
1510 | g_object_unref (object: task); |
1511 | |
1512 | while (g_atomic_int_get (&thread_state) == THREAD_STARTING) |
1513 | g_cond_wait (cond: &roc_init_cond, mutex: &roc_init_mutex); |
1514 | g_mutex_unlock (mutex: &roc_init_mutex); |
1515 | |
1516 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_RUNNING); |
1517 | g_assert (callback_ran == FALSE); |
1518 | |
1519 | g_cancellable_cancel (cancellable); |
1520 | g_mutex_unlock (mutex: &roc_finish_mutex); |
1521 | g_main_loop_run (loop); |
1522 | |
1523 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_COMPLETED); |
1524 | g_assert (callback_ran == TRUE); |
1525 | g_assert_true (notification_emitted); |
1526 | |
1527 | g_cancellable_reset (cancellable); |
1528 | |
1529 | /* If return-on-cancel is TRUE, it does return early */ |
1530 | callback_ran = FALSE; |
1531 | notification_emitted = FALSE; |
1532 | g_atomic_int_set (&thread_state, THREAD_STARTING); |
1533 | task = g_task_new (NULL, cancellable, callback: return_on_cancel_callback, callback_data: &callback_ran); |
1534 | g_object_weak_ref (G_OBJECT (task), notify: task_weak_notify, data: (gpointer)&weak_notify_ran); |
1535 | g_signal_connect (task, "notify::completed" , |
1536 | (GCallback) completed_cb, ¬ification_emitted); |
1537 | g_task_set_return_on_cancel (task, TRUE); |
1538 | |
1539 | g_task_set_task_data (task, task_data: (gpointer)&thread_state, NULL); |
1540 | g_mutex_lock (mutex: &roc_init_mutex); |
1541 | g_mutex_lock (mutex: &roc_finish_mutex); |
1542 | g_task_run_in_thread (task, task_func: return_on_cancel_thread); |
1543 | g_object_unref (object: task); |
1544 | |
1545 | while (g_atomic_int_get (&thread_state) == THREAD_STARTING) |
1546 | g_cond_wait (cond: &roc_init_cond, mutex: &roc_init_mutex); |
1547 | g_mutex_unlock (mutex: &roc_init_mutex); |
1548 | |
1549 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_RUNNING); |
1550 | g_assert (callback_ran == FALSE); |
1551 | |
1552 | g_cancellable_cancel (cancellable); |
1553 | g_main_loop_run (loop); |
1554 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_RUNNING); |
1555 | g_assert (callback_ran == TRUE); |
1556 | |
1557 | g_assert_false (g_atomic_int_get (&weak_notify_ran)); |
1558 | |
1559 | while (g_atomic_int_get (&thread_state) == THREAD_RUNNING) |
1560 | g_cond_wait (cond: &roc_finish_cond, mutex: &roc_finish_mutex); |
1561 | g_mutex_unlock (mutex: &roc_finish_mutex); |
1562 | |
1563 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_CANCELLED); |
1564 | g_mutex_lock (mutex: &run_in_thread_mutex); |
1565 | while (!g_atomic_int_get (&weak_notify_ran)) |
1566 | g_cond_wait (cond: &run_in_thread_cond, mutex: &run_in_thread_mutex); |
1567 | g_mutex_unlock (mutex: &run_in_thread_mutex); |
1568 | |
1569 | g_assert_true (notification_emitted); |
1570 | g_cancellable_reset (cancellable); |
1571 | |
1572 | /* If the task is already cancelled before it starts, it returns |
1573 | * immediately, but the thread func still runs. |
1574 | */ |
1575 | callback_ran = FALSE; |
1576 | notification_emitted = FALSE; |
1577 | g_atomic_int_set (&thread_state, THREAD_STARTING); |
1578 | task = g_task_new (NULL, cancellable, callback: return_on_cancel_callback, callback_data: &callback_ran); |
1579 | g_signal_connect (task, "notify::completed" , |
1580 | (GCallback) completed_cb, ¬ification_emitted); |
1581 | g_task_set_return_on_cancel (task, TRUE); |
1582 | |
1583 | g_cancellable_cancel (cancellable); |
1584 | |
1585 | g_task_set_task_data (task, task_data: (gpointer)&thread_state, NULL); |
1586 | g_mutex_lock (mutex: &roc_init_mutex); |
1587 | g_mutex_lock (mutex: &roc_finish_mutex); |
1588 | g_task_run_in_thread (task, task_func: return_on_cancel_thread); |
1589 | g_object_unref (object: task); |
1590 | |
1591 | g_main_loop_run (loop); |
1592 | g_assert (callback_ran == TRUE); |
1593 | |
1594 | while (g_atomic_int_get (&thread_state) == THREAD_STARTING) |
1595 | g_cond_wait (cond: &roc_init_cond, mutex: &roc_init_mutex); |
1596 | g_mutex_unlock (mutex: &roc_init_mutex); |
1597 | |
1598 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_RUNNING); |
1599 | |
1600 | while (g_atomic_int_get (&thread_state) == THREAD_RUNNING) |
1601 | g_cond_wait (cond: &roc_finish_cond, mutex: &roc_finish_mutex); |
1602 | g_mutex_unlock (mutex: &roc_finish_mutex); |
1603 | |
1604 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_CANCELLED); |
1605 | g_assert_true (notification_emitted); |
1606 | |
1607 | g_object_unref (object: cancellable); |
1608 | } |
1609 | |
1610 | /* test_return_on_cancel_sync */ |
1611 | |
1612 | static gpointer |
1613 | cancel_sync_runner_thread (gpointer task) |
1614 | { |
1615 | g_task_run_in_thread_sync (task, task_func: return_on_cancel_thread); |
1616 | return NULL; |
1617 | } |
1618 | |
1619 | static void |
1620 | test_return_on_cancel_sync (void) |
1621 | { |
1622 | GTask *task; |
1623 | GCancellable *cancellable; |
1624 | ThreadState thread_state; /* (atomic) */ |
1625 | GThread *runner_thread; |
1626 | gssize ret; |
1627 | GError *error = NULL; |
1628 | |
1629 | cancellable = g_cancellable_new (); |
1630 | |
1631 | /* If return-on-cancel is FALSE, the task does not return early. |
1632 | */ |
1633 | g_atomic_int_set (&thread_state, THREAD_STARTING); |
1634 | task = g_task_new (NULL, cancellable, callback: run_in_thread_sync_callback, NULL); |
1635 | |
1636 | g_task_set_task_data (task, task_data: (gpointer)&thread_state, NULL); |
1637 | g_mutex_lock (mutex: &roc_init_mutex); |
1638 | g_mutex_lock (mutex: &roc_finish_mutex); |
1639 | runner_thread = g_thread_new (name: "return-on-cancel-sync runner thread" , |
1640 | func: cancel_sync_runner_thread, data: task); |
1641 | |
1642 | while (g_atomic_int_get (&thread_state) == THREAD_STARTING) |
1643 | g_cond_wait (cond: &roc_init_cond, mutex: &roc_init_mutex); |
1644 | g_mutex_unlock (mutex: &roc_init_mutex); |
1645 | |
1646 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_RUNNING); |
1647 | |
1648 | g_cancellable_cancel (cancellable); |
1649 | g_mutex_unlock (mutex: &roc_finish_mutex); |
1650 | g_thread_join (thread: runner_thread); |
1651 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_COMPLETED); |
1652 | |
1653 | ret = g_task_propagate_int (task, error: &error); |
1654 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1655 | g_clear_error (err: &error); |
1656 | g_assert_cmpint (ret, ==, -1); |
1657 | |
1658 | g_object_unref (object: task); |
1659 | |
1660 | g_cancellable_reset (cancellable); |
1661 | |
1662 | /* If return-on-cancel is TRUE, it does return early */ |
1663 | g_atomic_int_set (&thread_state, THREAD_STARTING); |
1664 | task = g_task_new (NULL, cancellable, callback: run_in_thread_sync_callback, NULL); |
1665 | g_task_set_return_on_cancel (task, TRUE); |
1666 | |
1667 | g_task_set_task_data (task, task_data: (gpointer)&thread_state, NULL); |
1668 | g_mutex_lock (mutex: &roc_init_mutex); |
1669 | g_mutex_lock (mutex: &roc_finish_mutex); |
1670 | runner_thread = g_thread_new (name: "return-on-cancel-sync runner thread" , |
1671 | func: cancel_sync_runner_thread, data: task); |
1672 | |
1673 | while (g_atomic_int_get (&thread_state) == THREAD_STARTING) |
1674 | g_cond_wait (cond: &roc_init_cond, mutex: &roc_init_mutex); |
1675 | g_mutex_unlock (mutex: &roc_init_mutex); |
1676 | |
1677 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_RUNNING); |
1678 | |
1679 | g_cancellable_cancel (cancellable); |
1680 | g_thread_join (thread: runner_thread); |
1681 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_RUNNING); |
1682 | |
1683 | ret = g_task_propagate_int (task, error: &error); |
1684 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1685 | g_clear_error (err: &error); |
1686 | g_assert_cmpint (ret, ==, -1); |
1687 | |
1688 | g_object_unref (object: task); |
1689 | |
1690 | while (g_atomic_int_get (&thread_state) == THREAD_RUNNING) |
1691 | g_cond_wait (cond: &roc_finish_cond, mutex: &roc_finish_mutex); |
1692 | g_mutex_unlock (mutex: &roc_finish_mutex); |
1693 | |
1694 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_CANCELLED); |
1695 | |
1696 | g_cancellable_reset (cancellable); |
1697 | |
1698 | /* If the task is already cancelled before it starts, it returns |
1699 | * immediately, but the thread func still runs. |
1700 | */ |
1701 | g_atomic_int_set (&thread_state, THREAD_STARTING); |
1702 | task = g_task_new (NULL, cancellable, callback: run_in_thread_sync_callback, NULL); |
1703 | g_task_set_return_on_cancel (task, TRUE); |
1704 | |
1705 | g_cancellable_cancel (cancellable); |
1706 | |
1707 | g_task_set_task_data (task, task_data: (gpointer)&thread_state, NULL); |
1708 | g_mutex_lock (mutex: &roc_init_mutex); |
1709 | g_mutex_lock (mutex: &roc_finish_mutex); |
1710 | runner_thread = g_thread_new (name: "return-on-cancel-sync runner thread" , |
1711 | func: cancel_sync_runner_thread, data: task); |
1712 | |
1713 | g_thread_join (thread: runner_thread); |
1714 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_STARTING); |
1715 | |
1716 | ret = g_task_propagate_int (task, error: &error); |
1717 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1718 | g_clear_error (err: &error); |
1719 | g_assert_cmpint (ret, ==, -1); |
1720 | |
1721 | g_object_unref (object: task); |
1722 | |
1723 | while (g_atomic_int_get (&thread_state) == THREAD_STARTING) |
1724 | g_cond_wait (cond: &roc_init_cond, mutex: &roc_init_mutex); |
1725 | g_mutex_unlock (mutex: &roc_init_mutex); |
1726 | |
1727 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_RUNNING); |
1728 | |
1729 | while (g_atomic_int_get (&thread_state) == THREAD_RUNNING) |
1730 | g_cond_wait (cond: &roc_finish_cond, mutex: &roc_finish_mutex); |
1731 | g_mutex_unlock (mutex: &roc_finish_mutex); |
1732 | |
1733 | g_assert_cmpint (g_atomic_int_get (&thread_state), ==, THREAD_CANCELLED); |
1734 | |
1735 | g_object_unref (object: cancellable); |
1736 | } |
1737 | |
1738 | /* test_return_on_cancel_atomic: turning return-on-cancel on/off is |
1739 | * non-racy |
1740 | */ |
1741 | |
1742 | GMutex roca_mutex_1, roca_mutex_2; |
1743 | GCond roca_cond_1, roca_cond_2; |
1744 | |
1745 | static void |
1746 | return_on_cancel_atomic_callback (GObject *object, |
1747 | GAsyncResult *result, |
1748 | gpointer user_data) |
1749 | { |
1750 | gboolean *callback_ran = user_data; |
1751 | GError *error = NULL; |
1752 | gssize ret; |
1753 | |
1754 | g_assert (g_thread_self () == main_thread); |
1755 | |
1756 | g_assert (object == NULL); |
1757 | g_assert (g_task_is_valid (result, object)); |
1758 | g_assert (g_async_result_get_user_data (result) == user_data); |
1759 | g_assert (g_task_had_error (G_TASK (result))); |
1760 | g_assert_false (g_task_get_completed (G_TASK (result))); |
1761 | |
1762 | ret = g_task_propagate_int (G_TASK (result), error: &error); |
1763 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1764 | g_clear_error (err: &error); |
1765 | g_assert_cmpint (ret, ==, -1); |
1766 | |
1767 | g_assert (g_task_had_error (G_TASK (result))); |
1768 | |
1769 | *callback_ran = TRUE; |
1770 | g_main_loop_quit (loop); |
1771 | } |
1772 | |
1773 | static void |
1774 | return_on_cancel_atomic_thread (GTask *task, |
1775 | gpointer source_object, |
1776 | gpointer task_data, |
1777 | GCancellable *cancellable) |
1778 | { |
1779 | gint *state = task_data; /* (atomic) */ |
1780 | |
1781 | g_assert (source_object == g_task_get_source_object (task)); |
1782 | g_assert (task_data == g_task_get_task_data (task)); |
1783 | g_assert (cancellable == g_task_get_cancellable (task)); |
1784 | g_assert_false (g_task_get_completed (task)); |
1785 | |
1786 | g_assert (g_thread_self () != main_thread); |
1787 | g_assert_cmpint (g_atomic_int_get (state), ==, 0); |
1788 | |
1789 | g_mutex_lock (mutex: &roca_mutex_1); |
1790 | g_atomic_int_set (state, 1); |
1791 | g_cond_signal (cond: &roca_cond_1); |
1792 | g_mutex_unlock (mutex: &roca_mutex_1); |
1793 | |
1794 | g_mutex_lock (mutex: &roca_mutex_2); |
1795 | if (g_task_set_return_on_cancel (task, FALSE)) |
1796 | g_atomic_int_set (state, 2); |
1797 | else |
1798 | g_atomic_int_set (state, 3); |
1799 | g_cond_signal (cond: &roca_cond_2); |
1800 | g_mutex_unlock (mutex: &roca_mutex_2); |
1801 | |
1802 | g_mutex_lock (mutex: &roca_mutex_1); |
1803 | if (g_task_set_return_on_cancel (task, TRUE)) |
1804 | g_atomic_int_set (state, 4); |
1805 | else |
1806 | g_atomic_int_set (state, 5); |
1807 | g_cond_signal (cond: &roca_cond_1); |
1808 | g_mutex_unlock (mutex: &roca_mutex_1); |
1809 | |
1810 | g_mutex_lock (mutex: &roca_mutex_2); |
1811 | if (g_task_set_return_on_cancel (task, TRUE)) |
1812 | g_atomic_int_set (state, 6); |
1813 | else |
1814 | g_atomic_int_set (state, 7); |
1815 | g_cond_signal (cond: &roca_cond_2); |
1816 | g_mutex_unlock (mutex: &roca_mutex_2); |
1817 | |
1818 | g_task_return_int (task, result: magic); |
1819 | } |
1820 | |
1821 | static void |
1822 | test_return_on_cancel_atomic (void) |
1823 | { |
1824 | GTask *task; |
1825 | GCancellable *cancellable; |
1826 | gint state; /* (atomic) */ |
1827 | gboolean notification_emitted = FALSE; |
1828 | gboolean callback_ran; |
1829 | |
1830 | cancellable = g_cancellable_new (); |
1831 | g_mutex_lock (mutex: &roca_mutex_1); |
1832 | g_mutex_lock (mutex: &roca_mutex_2); |
1833 | |
1834 | /* If we don't cancel it, each set_return_on_cancel() call will succeed */ |
1835 | g_atomic_int_set (&state, 0); |
1836 | callback_ran = FALSE; |
1837 | task = g_task_new (NULL, cancellable, callback: return_on_cancel_atomic_callback, callback_data: &callback_ran); |
1838 | g_task_set_return_on_cancel (task, TRUE); |
1839 | g_signal_connect (task, "notify::completed" , |
1840 | (GCallback) completed_cb, ¬ification_emitted); |
1841 | |
1842 | g_task_set_task_data (task, task_data: (gpointer)&state, NULL); |
1843 | g_task_run_in_thread (task, task_func: return_on_cancel_atomic_thread); |
1844 | g_object_unref (object: task); |
1845 | |
1846 | g_assert_cmpint (g_atomic_int_get (&state), ==, 0); |
1847 | |
1848 | while (g_atomic_int_get (&state) == 0) |
1849 | g_cond_wait (cond: &roca_cond_1, mutex: &roca_mutex_1); |
1850 | g_assert_cmpint (g_atomic_int_get (&state), ==, 1); |
1851 | |
1852 | while (g_atomic_int_get (&state) == 1) |
1853 | g_cond_wait (cond: &roca_cond_2, mutex: &roca_mutex_2); |
1854 | g_assert_cmpint (g_atomic_int_get (&state), ==, 2); |
1855 | |
1856 | while (g_atomic_int_get (&state) == 2) |
1857 | g_cond_wait (cond: &roca_cond_1, mutex: &roca_mutex_1); |
1858 | g_assert_cmpint (g_atomic_int_get (&state), ==, 4); |
1859 | |
1860 | while (g_atomic_int_get (&state) == 4) |
1861 | g_cond_wait (cond: &roca_cond_2, mutex: &roca_mutex_2); |
1862 | g_assert_cmpint (g_atomic_int_get (&state), ==, 6); |
1863 | |
1864 | /* callback assumes there'll be a cancelled error */ |
1865 | g_cancellable_cancel (cancellable); |
1866 | |
1867 | g_assert (callback_ran == FALSE); |
1868 | g_main_loop_run (loop); |
1869 | g_assert (callback_ran == TRUE); |
1870 | g_assert_true (notification_emitted); |
1871 | |
1872 | g_cancellable_reset (cancellable); |
1873 | |
1874 | |
1875 | /* If we cancel while it's temporarily not return-on-cancel, the |
1876 | * task won't complete right away, and further |
1877 | * g_task_set_return_on_cancel() calls will return FALSE. |
1878 | */ |
1879 | g_atomic_int_set (&state, 0); |
1880 | callback_ran = FALSE; |
1881 | notification_emitted = FALSE; |
1882 | task = g_task_new (NULL, cancellable, callback: return_on_cancel_atomic_callback, callback_data: &callback_ran); |
1883 | g_task_set_return_on_cancel (task, TRUE); |
1884 | g_signal_connect (task, "notify::completed" , |
1885 | (GCallback) completed_cb, ¬ification_emitted); |
1886 | |
1887 | g_task_set_task_data (task, task_data: (gpointer)&state, NULL); |
1888 | g_task_run_in_thread (task, task_func: return_on_cancel_atomic_thread); |
1889 | |
1890 | g_assert_cmpint (g_atomic_int_get (&state), ==, 0); |
1891 | |
1892 | while (g_atomic_int_get (&state) == 0) |
1893 | g_cond_wait (cond: &roca_cond_1, mutex: &roca_mutex_1); |
1894 | g_assert_cmpint (g_atomic_int_get (&state), ==, 1); |
1895 | g_assert (g_task_get_return_on_cancel (task)); |
1896 | |
1897 | while (g_atomic_int_get (&state) == 1) |
1898 | g_cond_wait (cond: &roca_cond_2, mutex: &roca_mutex_2); |
1899 | g_assert_cmpint (g_atomic_int_get (&state), ==, 2); |
1900 | g_assert (!g_task_get_return_on_cancel (task)); |
1901 | |
1902 | g_cancellable_cancel (cancellable); |
1903 | g_idle_add (function: idle_quit_loop, NULL); |
1904 | g_main_loop_run (loop); |
1905 | g_assert (callback_ran == FALSE); |
1906 | |
1907 | while (g_atomic_int_get (&state) == 2) |
1908 | g_cond_wait (cond: &roca_cond_1, mutex: &roca_mutex_1); |
1909 | g_assert_cmpint (g_atomic_int_get (&state), ==, 5); |
1910 | g_assert (!g_task_get_return_on_cancel (task)); |
1911 | |
1912 | g_main_loop_run (loop); |
1913 | g_assert (callback_ran == TRUE); |
1914 | g_assert_true (notification_emitted); |
1915 | |
1916 | while (g_atomic_int_get (&state) == 5) |
1917 | g_cond_wait (cond: &roca_cond_2, mutex: &roca_mutex_2); |
1918 | g_assert_cmpint (g_atomic_int_get (&state), ==, 7); |
1919 | |
1920 | g_object_unref (object: cancellable); |
1921 | g_mutex_unlock (mutex: &roca_mutex_1); |
1922 | g_mutex_unlock (mutex: &roca_mutex_2); |
1923 | g_object_unref (object: task); |
1924 | } |
1925 | |
1926 | /* test_return_pointer: memory management of pointer returns */ |
1927 | |
1928 | static void |
1929 | test_return_pointer (void) |
1930 | { |
1931 | GObject *object, *ret; |
1932 | GTask *task; |
1933 | GCancellable *cancellable; |
1934 | GError *error = NULL; |
1935 | |
1936 | /* If we don't read back the return value, the task will |
1937 | * run its destroy notify. |
1938 | */ |
1939 | object = (GObject *)g_dummy_object_new (); |
1940 | g_assert_cmpint (object->ref_count, ==, 1); |
1941 | g_object_add_weak_pointer (object, weak_pointer_location: (gpointer *)&object); |
1942 | |
1943 | task = g_task_new (NULL, NULL, NULL, NULL); |
1944 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
1945 | g_task_return_pointer (task, result: object, result_destroy: g_object_unref); |
1946 | g_assert_cmpint (object->ref_count, ==, 1); |
1947 | |
1948 | /* Task and object are reffed until the :completed notification in idle. */ |
1949 | g_object_unref (object: task); |
1950 | g_assert_nonnull (task); |
1951 | g_assert_nonnull (object); |
1952 | |
1953 | wait_for_completed_notification (task); |
1954 | |
1955 | g_assert_null (task); |
1956 | g_assert_null (object); |
1957 | |
1958 | /* Likewise, if the return value is overwritten by an error */ |
1959 | object = (GObject *)g_dummy_object_new (); |
1960 | g_assert_cmpint (object->ref_count, ==, 1); |
1961 | g_object_add_weak_pointer (object, weak_pointer_location: (gpointer *)&object); |
1962 | |
1963 | cancellable = g_cancellable_new (); |
1964 | task = g_task_new (NULL, cancellable, NULL, NULL); |
1965 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
1966 | g_task_return_pointer (task, result: object, result_destroy: g_object_unref); |
1967 | g_assert_cmpint (object->ref_count, ==, 1); |
1968 | g_cancellable_cancel (cancellable); |
1969 | g_assert_cmpint (object->ref_count, ==, 1); |
1970 | |
1971 | ret = g_task_propagate_pointer (task, error: &error); |
1972 | g_assert (ret == NULL); |
1973 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1974 | g_clear_error (err: &error); |
1975 | g_assert_cmpint (object->ref_count, ==, 1); |
1976 | |
1977 | g_object_unref (object: task); |
1978 | g_object_unref (object: cancellable); |
1979 | g_assert_nonnull (task); |
1980 | g_assert_nonnull (object); |
1981 | |
1982 | wait_for_completed_notification (task); |
1983 | |
1984 | g_assert_null (task); |
1985 | g_assert_null (object); |
1986 | |
1987 | /* If we read back the return value, we steal its ref */ |
1988 | object = (GObject *)g_dummy_object_new (); |
1989 | g_assert_cmpint (object->ref_count, ==, 1); |
1990 | g_object_add_weak_pointer (object, weak_pointer_location: (gpointer *)&object); |
1991 | |
1992 | task = g_task_new (NULL, NULL, NULL, NULL); |
1993 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
1994 | g_task_return_pointer (task, result: object, result_destroy: g_object_unref); |
1995 | g_assert_cmpint (object->ref_count, ==, 1); |
1996 | |
1997 | ret = g_task_propagate_pointer (task, error: &error); |
1998 | g_assert_no_error (error); |
1999 | g_assert (ret == object); |
2000 | g_assert_cmpint (object->ref_count, ==, 1); |
2001 | |
2002 | g_object_unref (object: task); |
2003 | g_assert_nonnull (task); |
2004 | g_assert_cmpint (object->ref_count, ==, 1); |
2005 | g_object_unref (object); |
2006 | g_assert (object == NULL); |
2007 | |
2008 | wait_for_completed_notification (task); |
2009 | g_assert_null (task); |
2010 | } |
2011 | |
2012 | static void |
2013 | test_return_value (void) |
2014 | { |
2015 | GObject *object; |
2016 | GValue value = G_VALUE_INIT; |
2017 | GValue ret = G_VALUE_INIT; |
2018 | GTask *task; |
2019 | GError *error = NULL; |
2020 | |
2021 | object = (GObject *)g_dummy_object_new (); |
2022 | g_assert_cmpint (object->ref_count, ==, 1); |
2023 | g_object_add_weak_pointer (object, weak_pointer_location: (gpointer *)&object); |
2024 | |
2025 | g_value_init (value: &value, G_TYPE_OBJECT); |
2026 | g_value_set_object (value: &value, v_object: object); |
2027 | g_assert_cmpint (object->ref_count, ==, 2); |
2028 | |
2029 | task = g_task_new (NULL, NULL, NULL, NULL); |
2030 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
2031 | g_task_return_value (task, result: &value); |
2032 | g_assert_cmpint (object->ref_count, ==, 3); |
2033 | |
2034 | g_assert_true (g_task_propagate_value (task, &ret, &error)); |
2035 | g_assert_no_error (error); |
2036 | g_assert_true (g_value_get_object (&ret) == object); |
2037 | g_assert_cmpint (object->ref_count, ==, 3); |
2038 | |
2039 | g_object_unref (object: task); |
2040 | g_assert_nonnull (task); |
2041 | wait_for_completed_notification (task); |
2042 | g_assert_null (task); |
2043 | |
2044 | g_assert_cmpint (object->ref_count, ==, 3); |
2045 | g_value_unset (value: &ret); |
2046 | g_assert_cmpint (object->ref_count, ==, 2); |
2047 | g_value_unset (value: &value); |
2048 | g_assert_cmpint (object->ref_count, ==, 1); |
2049 | g_object_unref (object); |
2050 | g_assert_null (object); |
2051 | } |
2052 | |
2053 | /* test_object_keepalive: GTask takes a ref on its source object */ |
2054 | |
2055 | static GObject *keepalive_object; |
2056 | |
2057 | static void |
2058 | keepalive_callback (GObject *object, |
2059 | GAsyncResult *result, |
2060 | gpointer user_data) |
2061 | { |
2062 | gssize *result_out = user_data; |
2063 | GError *error = NULL; |
2064 | |
2065 | g_assert (object == keepalive_object); |
2066 | g_assert (g_task_is_valid (result, object)); |
2067 | g_assert (g_async_result_get_user_data (result) == user_data); |
2068 | g_assert (!g_task_had_error (G_TASK (result))); |
2069 | g_assert_false (g_task_get_completed (G_TASK (result))); |
2070 | |
2071 | *result_out = g_task_propagate_int (G_TASK (result), error: &error); |
2072 | g_assert_no_error (error); |
2073 | |
2074 | g_assert (!g_task_had_error (G_TASK (result))); |
2075 | |
2076 | g_main_loop_quit (loop); |
2077 | } |
2078 | |
2079 | static void |
2080 | test_object_keepalive (void) |
2081 | { |
2082 | GObject *object; |
2083 | GTask *task; |
2084 | gssize result; |
2085 | int ref_count; |
2086 | gboolean notification_emitted = FALSE; |
2087 | |
2088 | keepalive_object = object = (GObject *)g_dummy_object_new (); |
2089 | g_object_add_weak_pointer (object, weak_pointer_location: (gpointer *)&object); |
2090 | |
2091 | task = g_task_new (source_object: object, NULL, callback: keepalive_callback, callback_data: &result); |
2092 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
2093 | g_signal_connect (task, "notify::completed" , |
2094 | (GCallback) completed_cb, ¬ification_emitted); |
2095 | |
2096 | ref_count = object->ref_count; |
2097 | g_assert_cmpint (ref_count, >, 1); |
2098 | |
2099 | g_assert (g_task_get_source_object (task) == object); |
2100 | g_assert (g_async_result_get_source_object (G_ASYNC_RESULT (task)) == object); |
2101 | g_assert_cmpint (object->ref_count, ==, ref_count + 1); |
2102 | g_object_unref (object); |
2103 | |
2104 | g_object_unref (object); |
2105 | g_assert (object != NULL); |
2106 | |
2107 | g_task_return_int (task, result: magic); |
2108 | g_main_loop_run (loop); |
2109 | |
2110 | g_assert (object != NULL); |
2111 | g_assert_cmpint (result, ==, magic); |
2112 | g_assert_true (notification_emitted); |
2113 | |
2114 | g_object_unref (object: task); |
2115 | g_assert (task == NULL); |
2116 | g_assert (object == NULL); |
2117 | } |
2118 | |
2119 | /* test_legacy_error: legacy GSimpleAsyncResult handling */ |
2120 | static void test_legacy_error (void); |
2121 | |
2122 | static void |
2123 | legacy_error_callback (GObject *object, |
2124 | GAsyncResult *result, |
2125 | gpointer user_data) |
2126 | { |
2127 | gssize *result_out = user_data; |
2128 | GError *error = NULL; |
2129 | |
2130 | g_assert (object == NULL); |
2131 | g_assert (g_async_result_is_tagged (result, test_legacy_error)); |
2132 | g_assert (g_async_result_get_user_data (result) == user_data); |
2133 | |
2134 | if (g_async_result_legacy_propagate_error (res: result, error: &error)) |
2135 | { |
2136 | g_assert (!g_task_is_valid (result, object)); |
2137 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS; |
2138 | g_assert (g_simple_async_result_is_valid (result, object, test_legacy_error)); |
2139 | G_GNUC_END_IGNORE_DEPRECATIONS; |
2140 | |
2141 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); |
2142 | *result_out = -2; |
2143 | g_clear_error (err: &error); |
2144 | } |
2145 | else |
2146 | { |
2147 | g_assert (g_task_is_valid (result, object)); |
2148 | |
2149 | *result_out = g_task_propagate_int (G_TASK (result), NULL); |
2150 | /* Might be error, might not */ |
2151 | } |
2152 | |
2153 | g_main_loop_quit (loop); |
2154 | } |
2155 | |
2156 | static gboolean |
2157 | legacy_error_return (gpointer user_data) |
2158 | { |
2159 | if (G_IS_TASK (user_data)) |
2160 | { |
2161 | GTask *task = user_data; |
2162 | |
2163 | g_task_return_int (task, result: magic); |
2164 | g_object_unref (object: task); |
2165 | } |
2166 | else |
2167 | { |
2168 | GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); |
2169 | |
2170 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS; |
2171 | g_simple_async_result_set_error (simple, |
2172 | G_IO_ERROR, |
2173 | code: G_IO_ERROR_FAILED, |
2174 | format: "Failed" ); |
2175 | g_simple_async_result_complete (simple); |
2176 | G_GNUC_END_IGNORE_DEPRECATIONS; |
2177 | g_object_unref (object: simple); |
2178 | } |
2179 | |
2180 | return FALSE; |
2181 | } |
2182 | |
2183 | static void |
2184 | test_legacy_error (void) |
2185 | { |
2186 | GTask *task; |
2187 | GSimpleAsyncResult *simple; |
2188 | gssize result; |
2189 | |
2190 | /* GTask success */ |
2191 | task = g_task_new (NULL, NULL, callback: legacy_error_callback, callback_data: &result); |
2192 | g_task_set_source_tag (task, test_legacy_error); |
2193 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
2194 | |
2195 | g_idle_add (function: legacy_error_return, data: task); |
2196 | g_main_loop_run (loop); |
2197 | |
2198 | g_assert_cmpint (result, ==, magic); |
2199 | g_assert (task == NULL); |
2200 | |
2201 | /* GTask error */ |
2202 | task = g_task_new (NULL, NULL, callback: legacy_error_callback, callback_data: &result); |
2203 | g_task_set_source_tag (task, test_legacy_error); |
2204 | g_object_add_weak_pointer (G_OBJECT (task), weak_pointer_location: (gpointer *)&task); |
2205 | |
2206 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_FAILED, |
2207 | format: "Failed" ); |
2208 | g_object_unref (object: task); |
2209 | g_main_loop_run (loop); |
2210 | |
2211 | g_assert_cmpint (result, ==, -1); |
2212 | g_assert (task == NULL); |
2213 | |
2214 | /* GSimpleAsyncResult error */ |
2215 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS; |
2216 | simple = g_simple_async_result_new (NULL, callback: legacy_error_callback, user_data: &result, |
2217 | source_tag: test_legacy_error); |
2218 | G_GNUC_END_IGNORE_DEPRECATIONS; |
2219 | g_object_add_weak_pointer (G_OBJECT (simple), weak_pointer_location: (gpointer *)&simple); |
2220 | |
2221 | g_idle_add (function: legacy_error_return, data: simple); |
2222 | g_main_loop_run (loop); |
2223 | |
2224 | g_assert_cmpint (result, ==, -2); |
2225 | g_assert (simple == NULL); |
2226 | } |
2227 | |
2228 | /* Various helper functions for the return tests below. */ |
2229 | static void |
2230 | task_complete_cb (GObject *source, |
2231 | GAsyncResult *result, |
2232 | gpointer user_data) |
2233 | { |
2234 | GTask *task = G_TASK (result); |
2235 | guint *calls = user_data; |
2236 | |
2237 | g_assert_cmpint (++*calls, <=, 1); |
2238 | |
2239 | /* Propagate the result, so it’s removed from the task’s internal state. */ |
2240 | g_task_propagate_boolean (task, NULL); |
2241 | } |
2242 | |
2243 | static void |
2244 | return_twice (GTask *task) |
2245 | { |
2246 | gboolean error_first = GPOINTER_TO_UINT (g_task_get_task_data (task)); |
2247 | |
2248 | if (error_first) |
2249 | { |
2250 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_UNKNOWN, format: "oh no" ); |
2251 | g_task_return_boolean (task, TRUE); |
2252 | } |
2253 | else |
2254 | { |
2255 | g_task_return_boolean (task, TRUE); |
2256 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_UNKNOWN, format: "oh no" ); |
2257 | } |
2258 | } |
2259 | |
2260 | static gboolean |
2261 | idle_cb (gpointer user_data) |
2262 | { |
2263 | GTask *task = user_data; |
2264 | return_twice (task); |
2265 | g_object_unref (object: task); |
2266 | |
2267 | return G_SOURCE_REMOVE; |
2268 | } |
2269 | |
2270 | static void |
2271 | test_return_permutation (gboolean error_first, |
2272 | gboolean return_in_idle) |
2273 | { |
2274 | guint calls = 0; |
2275 | GTask *task = NULL; |
2276 | |
2277 | g_test_bug (bug_uri_snippet: "https://gitlab.gnome.org/GNOME/glib/issues/1525" ); |
2278 | |
2279 | task = g_task_new (NULL, NULL, callback: task_complete_cb, callback_data: &calls); |
2280 | g_task_set_task_data (task, GUINT_TO_POINTER (error_first), NULL); |
2281 | |
2282 | if (return_in_idle) |
2283 | g_idle_add (function: idle_cb, g_object_ref (task)); |
2284 | else |
2285 | return_twice (task); |
2286 | |
2287 | while (calls == 0) |
2288 | g_main_context_iteration (NULL, TRUE); |
2289 | |
2290 | g_assert_cmpint (calls, ==, 1); |
2291 | |
2292 | g_object_unref (object: task); |
2293 | } |
2294 | |
2295 | /* Test that calling g_task_return_boolean() after g_task_return_error(), when |
2296 | * returning in an idle callback, correctly results in a critical warning. */ |
2297 | static void |
2298 | test_return_in_idle_error_first (void) |
2299 | { |
2300 | if (g_test_subprocess ()) |
2301 | { |
2302 | test_return_permutation (TRUE, TRUE); |
2303 | return; |
2304 | } |
2305 | |
2306 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
2307 | g_test_trap_assert_failed (); |
2308 | g_test_trap_assert_stderr ("*CRITICAL*assertion '!task->ever_returned' failed*" ); |
2309 | } |
2310 | |
2311 | /* Test that calling g_task_return_error() after g_task_return_boolean(), when |
2312 | * returning in an idle callback, correctly results in a critical warning. */ |
2313 | static void |
2314 | test_return_in_idle_value_first (void) |
2315 | { |
2316 | if (g_test_subprocess ()) |
2317 | { |
2318 | test_return_permutation (FALSE, TRUE); |
2319 | return; |
2320 | } |
2321 | |
2322 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
2323 | g_test_trap_assert_failed (); |
2324 | g_test_trap_assert_stderr ("*CRITICAL*assertion '!task->ever_returned' failed*" ); |
2325 | } |
2326 | |
2327 | /* Test that calling g_task_return_boolean() after g_task_return_error(), when |
2328 | * returning synchronously, correctly results in a critical warning. */ |
2329 | static void |
2330 | test_return_error_first (void) |
2331 | { |
2332 | if (g_test_subprocess ()) |
2333 | { |
2334 | test_return_permutation (TRUE, FALSE); |
2335 | return; |
2336 | } |
2337 | |
2338 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
2339 | g_test_trap_assert_failed (); |
2340 | g_test_trap_assert_stderr ("*CRITICAL*assertion '!task->ever_returned' failed*" ); |
2341 | } |
2342 | |
2343 | /* Test that calling g_task_return_error() after g_task_return_boolean(), when |
2344 | * returning synchronously, correctly results in a critical warning. */ |
2345 | static void |
2346 | test_return_value_first (void) |
2347 | { |
2348 | if (g_test_subprocess ()) |
2349 | { |
2350 | test_return_permutation (FALSE, FALSE); |
2351 | return; |
2352 | } |
2353 | |
2354 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
2355 | g_test_trap_assert_failed (); |
2356 | g_test_trap_assert_stderr ("*CRITICAL*assertion '!task->ever_returned' failed*" ); |
2357 | } |
2358 | |
2359 | int |
2360 | main (int argc, char **argv) |
2361 | { |
2362 | int ret; |
2363 | |
2364 | g_test_init (argc: &argc, argv: &argv, NULL); |
2365 | |
2366 | loop = g_main_loop_new (NULL, FALSE); |
2367 | main_thread = g_thread_self (); |
2368 | magic = g_get_monotonic_time (); |
2369 | |
2370 | g_test_add_func (testpath: "/gtask/basic" , test_func: test_basic); |
2371 | g_test_add_func (testpath: "/gtask/error" , test_func: test_error); |
2372 | g_test_add_func (testpath: "/gtask/return-from-same-iteration" , test_func: test_return_from_same_iteration); |
2373 | g_test_add_func (testpath: "/gtask/return-from-toplevel" , test_func: test_return_from_toplevel); |
2374 | g_test_add_func (testpath: "/gtask/return-from-anon-thread" , test_func: test_return_from_anon_thread); |
2375 | g_test_add_func (testpath: "/gtask/return-from-wrong-thread" , test_func: test_return_from_wrong_thread); |
2376 | g_test_add_func (testpath: "/gtask/no-callback" , test_func: test_no_callback); |
2377 | g_test_add_func (testpath: "/gtask/report-error" , test_func: test_report_error); |
2378 | g_test_add_func (testpath: "/gtask/priority" , test_func: test_priority); |
2379 | g_test_add_func (testpath: "/gtask/name" , test_func: test_name); |
2380 | g_test_add_func (testpath: "/gtask/asynchronous-cancellation" , test_func: test_asynchronous_cancellation); |
2381 | g_test_add_func (testpath: "/gtask/check-cancellable" , test_func: test_check_cancellable); |
2382 | g_test_add_func (testpath: "/gtask/return-if-cancelled" , test_func: test_return_if_cancelled); |
2383 | g_test_add_func (testpath: "/gtask/run-in-thread" , test_func: test_run_in_thread); |
2384 | g_test_add_func (testpath: "/gtask/run-in-thread-sync" , test_func: test_run_in_thread_sync); |
2385 | g_test_add_func (testpath: "/gtask/run-in-thread-priority" , test_func: test_run_in_thread_priority); |
2386 | g_test_add_func (testpath: "/gtask/run-in-thread-nested" , test_func: test_run_in_thread_nested); |
2387 | g_test_add_func (testpath: "/gtask/run-in-thread-overflow" , test_func: test_run_in_thread_overflow); |
2388 | g_test_add_func (testpath: "/gtask/return-on-cancel" , test_func: test_return_on_cancel); |
2389 | g_test_add_func (testpath: "/gtask/return-on-cancel-sync" , test_func: test_return_on_cancel_sync); |
2390 | g_test_add_func (testpath: "/gtask/return-on-cancel-atomic" , test_func: test_return_on_cancel_atomic); |
2391 | g_test_add_func (testpath: "/gtask/return-pointer" , test_func: test_return_pointer); |
2392 | g_test_add_func (testpath: "/gtask/return-value" , test_func: test_return_value); |
2393 | g_test_add_func (testpath: "/gtask/object-keepalive" , test_func: test_object_keepalive); |
2394 | g_test_add_func (testpath: "/gtask/legacy-error" , test_func: test_legacy_error); |
2395 | g_test_add_func (testpath: "/gtask/return/in-idle/error-first" , test_func: test_return_in_idle_error_first); |
2396 | g_test_add_func (testpath: "/gtask/return/in-idle/value-first" , test_func: test_return_in_idle_value_first); |
2397 | g_test_add_func (testpath: "/gtask/return/error-first" , test_func: test_return_error_first); |
2398 | g_test_add_func (testpath: "/gtask/return/value-first" , test_func: test_return_value_first); |
2399 | |
2400 | ret = g_test_run(); |
2401 | |
2402 | g_main_loop_unref (loop); |
2403 | |
2404 | return ret; |
2405 | } |
2406 | |