nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* GIO - GLib Input, Output and Streaming Library |
2 | * |
||
3 | * Copyright 2011 Red Hat, Inc. |
||
4 | * |
||
5 | * This library is free software; you can redistribute it and/or |
||
6 | * modify it under the terms of the GNU Lesser General Public |
||
7 | * License as published by the Free Software Foundation; either |
||
8 | * version 2 of the License, or (at your option) any later version. |
||
9 | * |
||
10 | * This library is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
13 | * Lesser General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU Lesser General |
||
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
17 | */ |
||
18 | |||
19 | #include "config.h" |
||
20 | |||
21 | #include "gtask.h" |
||
22 | |||
23 | #include "gasyncresult.h" |
||
24 | #include "gcancellable.h" |
||
25 | #include "glib-private.h" |
||
26 | |||
27 | #include "glibintl.h" |
||
28 | |||
29 | /** |
||
30 | * SECTION:gtask |
||
31 | * @short_description: Cancellable synchronous or asynchronous task |
||
32 | * and result |
||
33 | * @include: gio/gio.h |
||
34 | * @see_also: #GAsyncResult |
||
35 | * |
||
36 | * A #GTask represents and manages a cancellable "task". |
||
37 | * |
||
38 | * ## Asynchronous operations |
||
39 | * |
||
40 | * The most common usage of #GTask is as a #GAsyncResult, to |
||
41 | * manage data during an asynchronous operation. You call |
||
42 | * g_task_new() in the "start" method, followed by |
||
43 | * g_task_set_task_data() and the like if you need to keep some |
||
44 | * additional data associated with the task, and then pass the |
||
45 | * task object around through your asynchronous operation. |
||
46 | * Eventually, you will call a method such as |
||
47 | * g_task_return_pointer() or g_task_return_error(), which will |
||
48 | * save the value you give it and then invoke the task's callback |
||
49 | * function (waiting until the next iteration of the main |
||
50 | * loop first, if necessary). The caller will pass the #GTask back |
||
51 | * to the operation's finish function (as a #GAsyncResult), and |
||
52 | * you can use g_task_propagate_pointer() or the like to extract |
||
53 | * the return value. |
||
54 | * |
||
55 | * Here is an example for using GTask as a GAsyncResult: |
||
56 | * |[<!-- language="C" --> |
||
57 | * typedef struct { |
||
58 | * CakeFrostingType frosting; |
||
59 | * char *message; |
||
60 | * } DecorationData; |
||
61 | * |
||
62 | * static void |
||
63 | * decoration_data_free (DecorationData *decoration) |
||
64 | * { |
||
65 | * g_free (decoration->message); |
||
66 | * g_slice_free (DecorationData, decoration); |
||
67 | * } |
||
68 | * |
||
69 | * static void |
||
70 | * baked_cb (Cake *cake, |
||
71 | * gpointer user_data) |
||
72 | * { |
||
73 | * GTask *task = user_data; |
||
74 | * DecorationData *decoration = g_task_get_task_data (task); |
||
75 | * GError *error = NULL; |
||
76 | * |
||
77 | * if (cake == NULL) |
||
78 | * { |
||
79 | * g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR, |
||
80 | * "Go to the supermarket"); |
||
81 | * g_object_unref (task); |
||
82 | * return; |
||
83 | * } |
||
84 | * |
||
85 | * if (!cake_decorate (cake, decoration->frosting, decoration->message, &error)) |
||
86 | * { |
||
87 | * g_object_unref (cake); |
||
88 | * // g_task_return_error() takes ownership of error |
||
89 | * g_task_return_error (task, error); |
||
90 | * g_object_unref (task); |
||
91 | * return; |
||
92 | * } |
||
93 | * |
||
94 | * g_task_return_pointer (task, cake, g_object_unref); |
||
95 | * g_object_unref (task); |
||
96 | * } |
||
97 | * |
||
98 | * void |
||
99 | * baker_bake_cake_async (Baker *self, |
||
100 | * guint radius, |
||
101 | * CakeFlavor flavor, |
||
102 | * CakeFrostingType frosting, |
||
103 | * const char *message, |
||
104 | * GCancellable *cancellable, |
||
105 | * GAsyncReadyCallback callback, |
||
106 | * gpointer user_data) |
||
107 | * { |
||
108 | * GTask *task; |
||
109 | * DecorationData *decoration; |
||
110 | * Cake *cake; |
||
111 | * |
||
112 | * task = g_task_new (self, cancellable, callback, user_data); |
||
113 | * if (radius < 3) |
||
114 | * { |
||
115 | * g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_TOO_SMALL, |
||
116 | * "%ucm radius cakes are silly", |
||
117 | * radius); |
||
118 | * g_object_unref (task); |
||
119 | * return; |
||
120 | * } |
||
121 | * |
||
122 | * cake = _baker_get_cached_cake (self, radius, flavor, frosting, message); |
||
123 | * if (cake != NULL) |
||
124 | * { |
||
125 | * // _baker_get_cached_cake() returns a reffed cake |
||
126 | * g_task_return_pointer (task, cake, g_object_unref); |
||
127 | * g_object_unref (task); |
||
128 | * return; |
||
129 | * } |
||
130 | * |
||
131 | * decoration = g_slice_new (DecorationData); |
||
132 | * decoration->frosting = frosting; |
||
133 | * decoration->message = g_strdup (message); |
||
134 | * g_task_set_task_data (task, decoration, (GDestroyNotify) decoration_data_free); |
||
135 | * |
||
136 | * _baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task); |
||
137 | * } |
||
138 | * |
||
139 | * Cake * |
||
140 | * baker_bake_cake_finish (Baker *self, |
||
141 | * GAsyncResult *result, |
||
142 | * GError **error) |
||
143 | * { |
||
144 | * g_return_val_if_fail (g_task_is_valid (result, self), NULL); |
||
145 | * |
||
146 | * return g_task_propagate_pointer (G_TASK (result), error); |
||
147 | * } |
||
148 | * ]| |
||
149 | * |
||
150 | * ## Chained asynchronous operations |
||
151 | * |
||
152 | * #GTask also tries to simplify asynchronous operations that |
||
153 | * internally chain together several smaller asynchronous |
||
154 | * operations. g_task_get_cancellable(), g_task_get_context(), |
||
155 | * and g_task_get_priority() allow you to get back the task's |
||
156 | * #GCancellable, #GMainContext, and [I/O priority][io-priority] |
||
157 | * when starting a new subtask, so you don't have to keep track |
||
158 | * of them yourself. g_task_attach_source() simplifies the case |
||
159 | * of waiting for a source to fire (automatically using the correct |
||
160 | * #GMainContext and priority). |
||
161 | * |
||
162 | * Here is an example for chained asynchronous operations: |
||
163 | * |[<!-- language="C" --> |
||
164 | * typedef struct { |
||
165 | * Cake *cake; |
||
166 | * CakeFrostingType frosting; |
||
167 | * char *message; |
||
168 | * } BakingData; |
||
169 | * |
||
170 | * static void |
||
171 | * decoration_data_free (BakingData *bd) |
||
172 | * { |
||
173 | * if (bd->cake) |
||
174 | * g_object_unref (bd->cake); |
||
175 | * g_free (bd->message); |
||
176 | * g_slice_free (BakingData, bd); |
||
177 | * } |
||
178 | * |
||
179 | * static void |
||
180 | * decorated_cb (Cake *cake, |
||
181 | * GAsyncResult *result, |
||
182 | * gpointer user_data) |
||
183 | * { |
||
184 | * GTask *task = user_data; |
||
185 | * GError *error = NULL; |
||
186 | * |
||
187 | * if (!cake_decorate_finish (cake, result, &error)) |
||
188 | * { |
||
189 | * g_object_unref (cake); |
||
190 | * g_task_return_error (task, error); |
||
191 | * g_object_unref (task); |
||
192 | * return; |
||
193 | * } |
||
194 | * |
||
195 | * // baking_data_free() will drop its ref on the cake, so we have to |
||
196 | * // take another here to give to the caller. |
||
197 | * g_task_return_pointer (task, g_object_ref (cake), g_object_unref); |
||
198 | * g_object_unref (task); |
||
199 | * } |
||
200 | * |
||
201 | * static gboolean |
||
202 | * decorator_ready (gpointer user_data) |
||
203 | * { |
||
204 | * GTask *task = user_data; |
||
205 | * BakingData *bd = g_task_get_task_data (task); |
||
206 | * |
||
207 | * cake_decorate_async (bd->cake, bd->frosting, bd->message, |
||
208 | * g_task_get_cancellable (task), |
||
209 | * decorated_cb, task); |
||
210 | * |
||
211 | * return G_SOURCE_REMOVE; |
||
212 | * } |
||
213 | * |
||
214 | * static void |
||
215 | * baked_cb (Cake *cake, |
||
216 | * gpointer user_data) |
||
217 | * { |
||
218 | * GTask *task = user_data; |
||
219 | * BakingData *bd = g_task_get_task_data (task); |
||
220 | * GError *error = NULL; |
||
221 | * |
||
222 | * if (cake == NULL) |
||
223 | * { |
||
224 | * g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR, |
||
225 | * "Go to the supermarket"); |
||
226 | * g_object_unref (task); |
||
227 | * return; |
||
228 | * } |
||
229 | * |
||
230 | * bd->cake = cake; |
||
231 | * |
||
232 | * // Bail out now if the user has already cancelled |
||
233 | * if (g_task_return_error_if_cancelled (task)) |
||
234 | * { |
||
235 | * g_object_unref (task); |
||
236 | * return; |
||
237 | * } |
||
238 | * |
||
239 | * if (cake_decorator_available (cake)) |
||
240 | * decorator_ready (task); |
||
241 | * else |
||
242 | * { |
||
243 | * GSource *source; |
||
244 | * |
||
245 | * source = cake_decorator_wait_source_new (cake); |
||
246 | * // Attach @source to @task's GMainContext and have it call |
||
247 | * // decorator_ready() when it is ready. |
||
248 | * g_task_attach_source (task, source, decorator_ready); |
||
249 | * g_source_unref (source); |
||
250 | * } |
||
251 | * } |
||
252 | * |
||
253 | * void |
||
254 | * baker_bake_cake_async (Baker *self, |
||
255 | * guint radius, |
||
256 | * CakeFlavor flavor, |
||
257 | * CakeFrostingType frosting, |
||
258 | * const char *message, |
||
259 | * gint priority, |
||
260 | * GCancellable *cancellable, |
||
261 | * GAsyncReadyCallback callback, |
||
262 | * gpointer user_data) |
||
263 | * { |
||
264 | * GTask *task; |
||
265 | * BakingData *bd; |
||
266 | * |
||
267 | * task = g_task_new (self, cancellable, callback, user_data); |
||
268 | * g_task_set_priority (task, priority); |
||
269 | * |
||
270 | * bd = g_slice_new0 (BakingData); |
||
271 | * bd->frosting = frosting; |
||
272 | * bd->message = g_strdup (message); |
||
273 | * g_task_set_task_data (task, bd, (GDestroyNotify) baking_data_free); |
||
274 | * |
||
275 | * _baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task); |
||
276 | * } |
||
277 | * |
||
278 | * Cake * |
||
279 | * baker_bake_cake_finish (Baker *self, |
||
280 | * GAsyncResult *result, |
||
281 | * GError **error) |
||
282 | * { |
||
283 | * g_return_val_if_fail (g_task_is_valid (result, self), NULL); |
||
284 | * |
||
285 | * return g_task_propagate_pointer (G_TASK (result), error); |
||
286 | * } |
||
287 | * ]| |
||
288 | * |
||
289 | * ## Asynchronous operations from synchronous ones |
||
290 | * |
||
291 | * You can use g_task_run_in_thread() to turn a synchronous |
||
292 | * operation into an asynchronous one, by running it in a thread |
||
293 | * which will then dispatch the result back to the caller's |
||
294 | * #GMainContext when it completes. |
||
295 | * |
||
296 | * Running a task in a thread: |
||
297 | * |[<!-- language="C" --> |
||
298 | * typedef struct { |
||
299 | * guint radius; |
||
300 | * CakeFlavor flavor; |
||
301 | * CakeFrostingType frosting; |
||
302 | * char *message; |
||
303 | * } CakeData; |
||
304 | * |
||
305 | * static void |
||
306 | * cake_data_free (CakeData *cake_data) |
||
307 | * { |
||
308 | * g_free (cake_data->message); |
||
309 | * g_slice_free (CakeData, cake_data); |
||
310 | * } |
||
311 | * |
||
312 | * static void |
||
313 | * bake_cake_thread (GTask *task, |
||
314 | * gpointer source_object, |
||
315 | * gpointer task_data, |
||
316 | * GCancellable *cancellable) |
||
317 | * { |
||
318 | * Baker *self = source_object; |
||
319 | * CakeData *cake_data = task_data; |
||
320 | * Cake *cake; |
||
321 | * GError *error = NULL; |
||
322 | * |
||
323 | * cake = bake_cake (baker, cake_data->radius, cake_data->flavor, |
||
324 | * cake_data->frosting, cake_data->message, |
||
325 | * cancellable, &error); |
||
326 | * if (cake) |
||
327 | * g_task_return_pointer (task, cake, g_object_unref); |
||
328 | * else |
||
329 | * g_task_return_error (task, error); |
||
330 | * } |
||
331 | * |
||
332 | * void |
||
333 | * baker_bake_cake_async (Baker *self, |
||
334 | * guint radius, |
||
335 | * CakeFlavor flavor, |
||
336 | * CakeFrostingType frosting, |
||
337 | * const char *message, |
||
338 | * GCancellable *cancellable, |
||
339 | * GAsyncReadyCallback callback, |
||
340 | * gpointer user_data) |
||
341 | * { |
||
342 | * CakeData *cake_data; |
||
343 | * GTask *task; |
||
344 | * |
||
345 | * cake_data = g_slice_new (CakeData); |
||
346 | * cake_data->radius = radius; |
||
347 | * cake_data->flavor = flavor; |
||
348 | * cake_data->frosting = frosting; |
||
349 | * cake_data->message = g_strdup (message); |
||
350 | * task = g_task_new (self, cancellable, callback, user_data); |
||
351 | * g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free); |
||
352 | * g_task_run_in_thread (task, bake_cake_thread); |
||
353 | * g_object_unref (task); |
||
354 | * } |
||
355 | * |
||
356 | * Cake * |
||
357 | * baker_bake_cake_finish (Baker *self, |
||
358 | * GAsyncResult *result, |
||
359 | * GError **error) |
||
360 | * { |
||
361 | * g_return_val_if_fail (g_task_is_valid (result, self), NULL); |
||
362 | * |
||
363 | * return g_task_propagate_pointer (G_TASK (result), error); |
||
364 | * } |
||
365 | * ]| |
||
366 | * |
||
367 | * ## Adding cancellability to uncancellable tasks |
||
368 | * |
||
369 | * Finally, g_task_run_in_thread() and g_task_run_in_thread_sync() |
||
370 | * can be used to turn an uncancellable operation into a |
||
371 | * cancellable one. If you call g_task_set_return_on_cancel(), |
||
372 | * passing %TRUE, then if the task's #GCancellable is cancelled, |
||
373 | * it will return control back to the caller immediately, while |
||
374 | * allowing the task thread to continue running in the background |
||
375 | * (and simply discarding its result when it finally does finish). |
||
376 | * Provided that the task thread is careful about how it uses |
||
377 | * locks and other externally-visible resources, this allows you |
||
378 | * to make "GLib-friendly" asynchronous and cancellable |
||
379 | * synchronous variants of blocking APIs. |
||
380 | * |
||
381 | * Cancelling a task: |
||
382 | * |[<!-- language="C" --> |
||
383 | * static void |
||
384 | * bake_cake_thread (GTask *task, |
||
385 | * gpointer source_object, |
||
386 | * gpointer task_data, |
||
387 | * GCancellable *cancellable) |
||
388 | * { |
||
389 | * Baker *self = source_object; |
||
390 | * CakeData *cake_data = task_data; |
||
391 | * Cake *cake; |
||
392 | * GError *error = NULL; |
||
393 | * |
||
394 | * cake = bake_cake (baker, cake_data->radius, cake_data->flavor, |
||
395 | * cake_data->frosting, cake_data->message, |
||
396 | * &error); |
||
397 | * if (error) |
||
398 | * { |
||
399 | * g_task_return_error (task, error); |
||
400 | * return; |
||
401 | * } |
||
402 | * |
||
403 | * // If the task has already been cancelled, then we don't want to add |
||
404 | * // the cake to the cake cache. Likewise, we don't want to have the |
||
405 | * // task get cancelled in the middle of updating the cache. |
||
406 | * // g_task_set_return_on_cancel() will return %TRUE here if it managed |
||
407 | * // to disable return-on-cancel, or %FALSE if the task was cancelled |
||
408 | * // before it could. |
||
409 | * if (g_task_set_return_on_cancel (task, FALSE)) |
||
410 | * { |
||
411 | * // If the caller cancels at this point, their |
||
412 | * // GAsyncReadyCallback won't be invoked until we return, |
||
413 | * // so we don't have to worry that this code will run at |
||
414 | * // the same time as that code does. But if there were |
||
415 | * // other functions that might look at the cake cache, |
||
416 | * // then we'd probably need a GMutex here as well. |
||
417 | * baker_add_cake_to_cache (baker, cake); |
||
418 | * g_task_return_pointer (task, cake, g_object_unref); |
||
419 | * } |
||
420 | * } |
||
421 | * |
||
422 | * void |
||
423 | * baker_bake_cake_async (Baker *self, |
||
424 | * guint radius, |
||
425 | * CakeFlavor flavor, |
||
426 | * CakeFrostingType frosting, |
||
427 | * const char *message, |
||
428 | * GCancellable *cancellable, |
||
429 | * GAsyncReadyCallback callback, |
||
430 | * gpointer user_data) |
||
431 | * { |
||
432 | * CakeData *cake_data; |
||
433 | * GTask *task; |
||
434 | * |
||
435 | * cake_data = g_slice_new (CakeData); |
||
436 | * |
||
437 | * ... |
||
438 | * |
||
439 | * task = g_task_new (self, cancellable, callback, user_data); |
||
440 | * g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free); |
||
441 | * g_task_set_return_on_cancel (task, TRUE); |
||
442 | * g_task_run_in_thread (task, bake_cake_thread); |
||
443 | * } |
||
444 | * |
||
445 | * Cake * |
||
446 | * baker_bake_cake_sync (Baker *self, |
||
447 | * guint radius, |
||
448 | * CakeFlavor flavor, |
||
449 | * CakeFrostingType frosting, |
||
450 | * const char *message, |
||
451 | * GCancellable *cancellable, |
||
452 | * GError **error) |
||
453 | * { |
||
454 | * CakeData *cake_data; |
||
455 | * GTask *task; |
||
456 | * Cake *cake; |
||
457 | * |
||
458 | * cake_data = g_slice_new (CakeData); |
||
459 | * |
||
460 | * ... |
||
461 | * |
||
462 | * task = g_task_new (self, cancellable, NULL, NULL); |
||
463 | * g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free); |
||
464 | * g_task_set_return_on_cancel (task, TRUE); |
||
465 | * g_task_run_in_thread_sync (task, bake_cake_thread); |
||
466 | * |
||
467 | * cake = g_task_propagate_pointer (task, error); |
||
468 | * g_object_unref (task); |
||
469 | * return cake; |
||
470 | * } |
||
471 | * ]| |
||
472 | * |
||
473 | * ## Porting from GSimpleAsyncResult |
||
474 | * |
||
475 | * #GTask's API attempts to be simpler than #GSimpleAsyncResult's |
||
476 | * in several ways: |
||
477 | * - You can save task-specific data with g_task_set_task_data(), and |
||
478 | * retrieve it later with g_task_get_task_data(). This replaces the |
||
479 | * abuse of g_simple_async_result_set_op_res_gpointer() for the same |
||
480 | * purpose with #GSimpleAsyncResult. |
||
481 | * - In addition to the task data, #GTask also keeps track of the |
||
482 | * [priority][io-priority], #GCancellable, and |
||
483 | * #GMainContext associated with the task, so tasks that consist of |
||
484 | * a chain of simpler asynchronous operations will have easy access |
||
485 | * to those values when starting each sub-task. |
||
486 | * - g_task_return_error_if_cancelled() provides simplified |
||
487 | * handling for cancellation. In addition, cancellation |
||
488 | * overrides any other #GTask return value by default, like |
||
489 | * #GSimpleAsyncResult does when |
||
490 | * g_simple_async_result_set_check_cancellable() is called. |
||
491 | * (You can use g_task_set_check_cancellable() to turn off that |
||
492 | * behavior.) On the other hand, g_task_run_in_thread() |
||
493 | * guarantees that it will always run your |
||
494 | * `task_func`, even if the task's #GCancellable |
||
495 | * is already cancelled before the task gets a chance to run; |
||
496 | * you can start your `task_func` with a |
||
497 | * g_task_return_error_if_cancelled() check if you need the |
||
498 | * old behavior. |
||
499 | * - The "return" methods (eg, g_task_return_pointer()) |
||
500 | * automatically cause the task to be "completed" as well, and |
||
501 | * there is no need to worry about the "complete" vs "complete |
||
502 | * in idle" distinction. (#GTask automatically figures out |
||
503 | * whether the task's callback can be invoked directly, or |
||
504 | * if it needs to be sent to another #GMainContext, or delayed |
||
505 | * until the next iteration of the current #GMainContext.) |
||
506 | * - The "finish" functions for #GTask-based operations are generally |
||
507 | * much simpler than #GSimpleAsyncResult ones, normally consisting |
||
508 | * of only a single call to g_task_propagate_pointer() or the like. |
||
509 | * Since g_task_propagate_pointer() "steals" the return value from |
||
510 | * the #GTask, it is not necessary to juggle pointers around to |
||
511 | * prevent it from being freed twice. |
||
512 | * - With #GSimpleAsyncResult, it was common to call |
||
513 | * g_simple_async_result_propagate_error() from the |
||
514 | * `_finish()` wrapper function, and have |
||
515 | * virtual method implementations only deal with successful |
||
516 | * returns. This behavior is deprecated, because it makes it |
||
517 | * difficult for a subclass to chain to a parent class's async |
||
518 | * methods. Instead, the wrapper function should just be a |
||
519 | * simple wrapper, and the virtual method should call an |
||
520 | * appropriate `g_task_propagate_` function. |
||
521 | * Note that wrapper methods can now use |
||
522 | * g_async_result_legacy_propagate_error() to do old-style |
||
523 | * #GSimpleAsyncResult error-returning behavior, and |
||
524 | * g_async_result_is_tagged() to check if a result is tagged as |
||
525 | * having come from the `_async()` wrapper |
||
526 | * function (for "short-circuit" results, such as when passing |
||
527 | * 0 to g_input_stream_read_async()). |
||
528 | */ |
||
529 | |||
530 | /** |
||
531 | * GTask: |
||
532 | * |
||
533 | * The opaque object representing a synchronous or asynchronous task |
||
534 | * and its result. |
||
535 | */ |
||
536 | |||
537 | struct _GTask { |
||
538 | GObject parent_instance; |
||
539 | |||
540 | gpointer source_object; |
||
541 | gpointer source_tag; |
||
542 | |||
543 | gpointer task_data; |
||
544 | GDestroyNotify task_data_destroy; |
||
545 | |||
546 | GMainContext *context; |
||
547 | gint64 creation_time; |
||
548 | gint priority; |
||
549 | GCancellable *cancellable; |
||
550 | gboolean check_cancellable; |
||
551 | |||
552 | GAsyncReadyCallback callback; |
||
553 | gpointer callback_data; |
||
554 | gboolean completed; |
||
555 | |||
556 | GTaskThreadFunc task_func; |
||
557 | GMutex lock; |
||
558 | GCond cond; |
||
559 | gboolean return_on_cancel; |
||
560 | gboolean thread_cancelled; |
||
561 | gboolean synchronous; |
||
562 | gboolean thread_complete; |
||
563 | gboolean blocking_other_task; |
||
564 | |||
565 | GError *error; |
||
566 | union { |
||
567 | gpointer pointer; |
||
568 | gssize size; |
||
569 | gboolean boolean; |
||
570 | } result; |
||
571 | GDestroyNotify result_destroy; |
||
572 | gboolean result_set; |
||
573 | }; |
||
574 | |||
575 | #define G_TASK_IS_THREADED(task) ((task)->task_func != NULL) |
||
576 | |||
577 | struct _GTaskClass |
||
578 | { |
||
579 | GObjectClass parent_class; |
||
580 | }; |
||
581 | |||
582 | typedef enum |
||
583 | { |
||
584 | PROP_COMPLETED = 1, |
||
585 | } GTaskProperty; |
||
586 | |||
587 | static void g_task_async_result_iface_init (GAsyncResultIface *iface); |
||
588 | static void g_task_thread_pool_init (void); |
||
589 | |||
590 | G_DEFINE_TYPE_WITH_CODE (GTask, g_task, G_TYPE_OBJECT, |
||
591 | G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, |
||
592 | g_task_async_result_iface_init); |
||
593 | g_task_thread_pool_init ();) |
||
594 | |||
595 | static GThreadPool *task_pool; |
||
596 | static GMutex task_pool_mutex; |
||
597 | static GPrivate task_private = G_PRIVATE_INIT (NULL); |
||
598 | static GSource *task_pool_manager; |
||
599 | static guint64 task_wait_time; |
||
600 | static gint tasks_running; |
||
601 | |||
602 | /* When the task pool fills up and blocks, and the program keeps |
||
603 | * queueing more tasks, we will slowly add more threads to the pool |
||
604 | * (in case the existing tasks are trying to queue subtasks of their |
||
605 | * own) until tasks start completing again. These "overflow" threads |
||
606 | * will only run one task apiece, and then exit, so the pool will |
||
607 | * eventually get back down to its base size. |
||
608 | * |
||
609 | * The base and multiplier below gives us 10 extra threads after about |
||
610 | * a second of blocking, 30 after 5 seconds, 100 after a minute, and |
||
611 | * 200 after 20 minutes. |
||
612 | */ |
||
613 | #define G_TASK_POOL_SIZE 10 |
||
614 | #define G_TASK_WAIT_TIME_BASE 100000 |
||
615 | #define G_TASK_WAIT_TIME_MULTIPLIER 1.03 |
||
616 | #define G_TASK_WAIT_TIME_MAX (30 * 60 * 1000000) |
||
617 | |||
618 | static void |
||
619 | g_task_init (GTask *task) |
||
620 | { |
||
621 | task->check_cancellable = TRUE; |
||
622 | } |
||
623 | |||
624 | static void |
||
625 | g_task_finalize (GObject *object) |
||
626 | { |
||
627 | GTask *task = G_TASK (object); |
||
628 | |||
629 | g_clear_object (&task->source_object); |
||
630 | g_clear_object (&task->cancellable); |
||
631 | |||
632 | if (task->context) |
||
633 | g_main_context_unref (task->context); |
||
634 | |||
635 | if (task->task_data_destroy) |
||
636 | task->task_data_destroy (task->task_data); |
||
637 | |||
638 | if (task->result_destroy && task->result.pointer) |
||
639 | task->result_destroy (task->result.pointer); |
||
640 | |||
641 | if (task->error) |
||
642 | g_error_free (task->error); |
||
643 | |||
644 | if (G_TASK_IS_THREADED (task)) |
||
645 | { |
||
646 | g_mutex_clear (&task->lock); |
||
647 | g_cond_clear (&task->cond); |
||
648 | } |
||
649 | |||
650 | G_OBJECT_CLASS (g_task_parent_class)->finalize (object); |
||
651 | } |
||
652 | |||
653 | /** |
||
654 | * g_task_new: |
||
655 | * @source_object: (allow-none) (type GObject): the #GObject that owns |
||
656 | * this task, or %NULL. |
||
657 | * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore. |
||
658 | * @callback: (scope async): a #GAsyncReadyCallback. |
||
659 | * @callback_data: (closure): user data passed to @callback. |
||
660 | * |
||
661 | * Creates a #GTask acting on @source_object, which will eventually be |
||
662 | * used to invoke @callback in the current |
||
663 | * [thread-default main context][g-main-context-push-thread-default]. |
||
664 | * |
||
665 | * Call this in the "start" method of your asynchronous method, and |
||
666 | * pass the #GTask around throughout the asynchronous operation. You |
||
667 | * can use g_task_set_task_data() to attach task-specific data to the |
||
668 | * object, which you can retrieve later via g_task_get_task_data(). |
||
669 | * |
||
670 | * By default, if @cancellable is cancelled, then the return value of |
||
671 | * the task will always be %G_IO_ERROR_CANCELLED, even if the task had |
||
672 | * already completed before the cancellation. This allows for |
||
673 | * simplified handling in cases where cancellation may imply that |
||
674 | * other objects that the task depends on have been destroyed. If you |
||
675 | * do not want this behavior, you can use |
||
676 | * g_task_set_check_cancellable() to change it. |
||
677 | * |
||
678 | * Returns: a #GTask. |
||
679 | * |
||
680 | * Since: 2.36 |
||
681 | */ |
||
682 | GTask * |
||
683 | g_task_new (gpointer source_object, |
||
684 | GCancellable *cancellable, |
||
685 | GAsyncReadyCallback callback, |
||
686 | gpointer callback_data) |
||
687 | { |
||
688 | GTask *task; |
||
689 | GSource *source; |
||
690 | |||
691 | task = g_object_new (G_TYPE_TASK, NULL); |
||
692 | task->source_object = source_object ? g_object_ref (source_object) : NULL; |
||
693 | task->cancellable = cancellable ? g_object_ref (cancellable) : NULL; |
||
694 | task->callback = callback; |
||
695 | task->callback_data = callback_data; |
||
696 | task->context = g_main_context_ref_thread_default (); |
||
697 | |||
698 | source = g_main_current_source (); |
||
699 | if (source) |
||
700 | task->creation_time = g_source_get_time (source); |
||
701 | |||
702 | return task; |
||
703 | } |
||
704 | |||
705 | /** |
||
706 | * g_task_report_error: |
||
707 | * @source_object: (allow-none) (type GObject): the #GObject that owns |
||
708 | * this task, or %NULL. |
||
709 | * @callback: (scope async): a #GAsyncReadyCallback. |
||
710 | * @callback_data: (closure): user data passed to @callback. |
||
711 | * @source_tag: an opaque pointer indicating the source of this task |
||
712 | * @error: (transfer full): error to report |
||
713 | * |
||
714 | * Creates a #GTask and then immediately calls g_task_return_error() |
||
715 | * on it. Use this in the wrapper function of an asynchronous method |
||
716 | * when you want to avoid even calling the virtual method. You can |
||
717 | * then use g_async_result_is_tagged() in the finish method wrapper to |
||
718 | * check if the result there is tagged as having been created by the |
||
719 | * wrapper method, and deal with it appropriately if so. |
||
720 | * |
||
721 | * See also g_task_report_new_error(). |
||
722 | * |
||
723 | * Since: 2.36 |
||
724 | */ |
||
725 | void |
||
726 | g_task_report_error (gpointer source_object, |
||
727 | GAsyncReadyCallback callback, |
||
728 | gpointer callback_data, |
||
729 | gpointer source_tag, |
||
730 | GError *error) |
||
731 | { |
||
732 | GTask *task; |
||
733 | |||
734 | task = g_task_new (source_object, NULL, callback, callback_data); |
||
735 | g_task_set_source_tag (task, source_tag); |
||
736 | g_task_return_error (task, error); |
||
737 | g_object_unref (task); |
||
738 | } |
||
739 | |||
740 | /** |
||
741 | * g_task_report_new_error: |
||
742 | * @source_object: (allow-none) (type GObject): the #GObject that owns |
||
743 | * this task, or %NULL. |
||
744 | * @callback: (scope async): a #GAsyncReadyCallback. |
||
745 | * @callback_data: (closure): user data passed to @callback. |
||
746 | * @source_tag: an opaque pointer indicating the source of this task |
||
747 | * @domain: a #GQuark. |
||
748 | * @code: an error code. |
||
749 | * @format: a string with format characters. |
||
750 | * @...: a list of values to insert into @format. |
||
751 | * |
||
752 | * Creates a #GTask and then immediately calls |
||
753 | * g_task_return_new_error() on it. Use this in the wrapper function |
||
754 | * of an asynchronous method when you want to avoid even calling the |
||
755 | * virtual method. You can then use g_async_result_is_tagged() in the |
||
756 | * finish method wrapper to check if the result there is tagged as |
||
757 | * having been created by the wrapper method, and deal with it |
||
758 | * appropriately if so. |
||
759 | * |
||
760 | * See also g_task_report_error(). |
||
761 | * |
||
762 | * Since: 2.36 |
||
763 | */ |
||
764 | void |
||
765 | g_task_report_new_error (gpointer source_object, |
||
766 | GAsyncReadyCallback callback, |
||
767 | gpointer callback_data, |
||
768 | gpointer source_tag, |
||
769 | GQuark domain, |
||
770 | gint code, |
||
771 | const char *format, |
||
772 | ...) |
||
773 | { |
||
774 | GError *error; |
||
775 | va_list ap; |
||
776 | |||
777 | va_start (ap, format); |
||
778 | error = g_error_new_valist (domain, code, format, ap); |
||
779 | va_end (ap); |
||
780 | |||
781 | g_task_report_error (source_object, callback, callback_data, |
||
782 | source_tag, error); |
||
783 | } |
||
784 | |||
785 | /** |
||
786 | * g_task_set_task_data: |
||
787 | * @task: the #GTask |
||
788 | * @task_data: (allow-none): task-specific data |
||
789 | * @task_data_destroy: (allow-none): #GDestroyNotify for @task_data |
||
790 | * |
||
791 | * Sets @task's task data (freeing the existing task data, if any). |
||
792 | * |
||
793 | * Since: 2.36 |
||
794 | */ |
||
795 | void |
||
796 | g_task_set_task_data (GTask *task, |
||
797 | gpointer task_data, |
||
798 | GDestroyNotify task_data_destroy) |
||
799 | { |
||
800 | if (task->task_data_destroy) |
||
801 | task->task_data_destroy (task->task_data); |
||
802 | |||
803 | task->task_data = task_data; |
||
804 | task->task_data_destroy = task_data_destroy; |
||
805 | } |
||
806 | |||
807 | /** |
||
808 | * g_task_set_priority: |
||
809 | * @task: the #GTask |
||
810 | * @priority: the [priority][io-priority] of the request |
||
811 | * |
||
812 | * Sets @task's priority. If you do not call this, it will default to |
||
813 | * %G_PRIORITY_DEFAULT. |
||
814 | * |
||
815 | * This will affect the priority of #GSources created with |
||
816 | * g_task_attach_source() and the scheduling of tasks run in threads, |
||
817 | * and can also be explicitly retrieved later via |
||
818 | * g_task_get_priority(). |
||
819 | * |
||
820 | * Since: 2.36 |
||
821 | */ |
||
822 | void |
||
823 | g_task_set_priority (GTask *task, |
||
824 | gint priority) |
||
825 | { |
||
826 | task->priority = priority; |
||
827 | } |
||
828 | |||
829 | /** |
||
830 | * g_task_set_check_cancellable: |
||
831 | * @task: the #GTask |
||
832 | * @check_cancellable: whether #GTask will check the state of |
||
833 | * its #GCancellable for you. |
||
834 | * |
||
835 | * Sets or clears @task's check-cancellable flag. If this is %TRUE |
||
836 | * (the default), then g_task_propagate_pointer(), etc, and |
||
837 | * g_task_had_error() will check the task's #GCancellable first, and |
||
838 | * if it has been cancelled, then they will consider the task to have |
||
839 | * returned an "Operation was cancelled" error |
||
840 | * (%G_IO_ERROR_CANCELLED), regardless of any other error or return |
||
841 | * value the task may have had. |
||
842 | * |
||
843 | * If @check_cancellable is %FALSE, then the #GTask will not check the |
||
844 | * cancellable itself, and it is up to @task's owner to do this (eg, |
||
845 | * via g_task_return_error_if_cancelled()). |
||
846 | * |
||
847 | * If you are using g_task_set_return_on_cancel() as well, then |
||
848 | * you must leave check-cancellable set %TRUE. |
||
849 | * |
||
850 | * Since: 2.36 |
||
851 | */ |
||
852 | void |
||
853 | g_task_set_check_cancellable (GTask *task, |
||
854 | gboolean check_cancellable) |
||
855 | { |
||
856 | g_return_if_fail (check_cancellable || !task->return_on_cancel); |
||
857 | |||
858 | task->check_cancellable = check_cancellable; |
||
859 | } |
||
860 | |||
861 | static void g_task_thread_complete (GTask *task); |
||
862 | |||
863 | /** |
||
864 | * g_task_set_return_on_cancel: |
||
865 | * @task: the #GTask |
||
866 | * @return_on_cancel: whether the task returns automatically when |
||
867 | * it is cancelled. |
||
868 | * |
||
869 | * Sets or clears @task's return-on-cancel flag. This is only |
||
870 | * meaningful for tasks run via g_task_run_in_thread() or |
||
871 | * g_task_run_in_thread_sync(). |
||
872 | * |
||
873 | * If @return_on_cancel is %TRUE, then cancelling @task's |
||
874 | * #GCancellable will immediately cause it to return, as though the |
||
875 | * task's #GTaskThreadFunc had called |
||
876 | * g_task_return_error_if_cancelled() and then returned. |
||
877 | * |
||
878 | * This allows you to create a cancellable wrapper around an |
||
879 | * uninterruptable function. The #GTaskThreadFunc just needs to be |
||
880 | * careful that it does not modify any externally-visible state after |
||
881 | * it has been cancelled. To do that, the thread should call |
||
882 | * g_task_set_return_on_cancel() again to (atomically) set |
||
883 | * return-on-cancel %FALSE before making externally-visible changes; |
||
884 | * if the task gets cancelled before the return-on-cancel flag could |
||
885 | * be changed, g_task_set_return_on_cancel() will indicate this by |
||
886 | * returning %FALSE. |
||
887 | * |
||
888 | * You can disable and re-enable this flag multiple times if you wish. |
||
889 | * If the task's #GCancellable is cancelled while return-on-cancel is |
||
890 | * %FALSE, then calling g_task_set_return_on_cancel() to set it %TRUE |
||
891 | * again will cause the task to be cancelled at that point. |
||
892 | * |
||
893 | * If the task's #GCancellable is already cancelled before you call |
||
894 | * g_task_run_in_thread()/g_task_run_in_thread_sync(), then the |
||
895 | * #GTaskThreadFunc will still be run (for consistency), but the task |
||
896 | * will also be completed right away. |
||
897 | * |
||
898 | * Returns: %TRUE if @task's return-on-cancel flag was changed to |
||
899 | * match @return_on_cancel. %FALSE if @task has already been |
||
900 | * cancelled. |
||
901 | * |
||
902 | * Since: 2.36 |
||
903 | */ |
||
904 | gboolean |
||
905 | g_task_set_return_on_cancel (GTask *task, |
||
906 | gboolean return_on_cancel) |
||
907 | { |
||
908 | g_return_val_if_fail (task->check_cancellable || !return_on_cancel, FALSE); |
||
909 | |||
910 | if (!G_TASK_IS_THREADED (task)) |
||
911 | { |
||
912 | task->return_on_cancel = return_on_cancel; |
||
913 | return TRUE; |
||
914 | } |
||
915 | |||
916 | g_mutex_lock (&task->lock); |
||
917 | if (task->thread_cancelled) |
||
918 | { |
||
919 | if (return_on_cancel && !task->return_on_cancel) |
||
920 | { |
||
921 | g_mutex_unlock (&task->lock); |
||
922 | g_task_thread_complete (task); |
||
923 | } |
||
924 | else |
||
925 | g_mutex_unlock (&task->lock); |
||
926 | return FALSE; |
||
927 | } |
||
928 | task->return_on_cancel = return_on_cancel; |
||
929 | g_mutex_unlock (&task->lock); |
||
930 | |||
931 | return TRUE; |
||
932 | } |
||
933 | |||
934 | /** |
||
935 | * g_task_set_source_tag: |
||
936 | * @task: the #GTask |
||
937 | * @source_tag: an opaque pointer indicating the source of this task |
||
938 | * |
||
939 | * Sets @task's source tag. You can use this to tag a task return |
||
940 | * value with a particular pointer (usually a pointer to the function |
||
941 | * doing the tagging) and then later check it using |
||
942 | * g_task_get_source_tag() (or g_async_result_is_tagged()) in the |
||
943 | * task's "finish" function, to figure out if the response came from a |
||
944 | * particular place. |
||
945 | * |
||
946 | * Since: 2.36 |
||
947 | */ |
||
948 | void |
||
949 | g_task_set_source_tag (GTask *task, |
||
950 | gpointer source_tag) |
||
951 | { |
||
952 | task->source_tag = source_tag; |
||
953 | } |
||
954 | |||
955 | /** |
||
956 | * g_task_get_source_object: |
||
957 | * @task: a #GTask |
||
958 | * |
||
959 | * Gets the source object from @task. Like |
||
960 | * g_async_result_get_source_object(), but does not ref the object. |
||
961 | * |
||
962 | * Returns: (transfer none) (type GObject): @task's source object, or %NULL |
||
963 | * |
||
964 | * Since: 2.36 |
||
965 | */ |
||
966 | gpointer |
||
967 | g_task_get_source_object (GTask *task) |
||
968 | { |
||
969 | return task->source_object; |
||
970 | } |
||
971 | |||
972 | static GObject * |
||
973 | g_task_ref_source_object (GAsyncResult *res) |
||
974 | { |
||
975 | GTask *task = G_TASK (res); |
||
976 | |||
977 | if (task->source_object) |
||
978 | return g_object_ref (task->source_object); |
||
979 | else |
||
980 | return NULL; |
||
981 | } |
||
982 | |||
983 | /** |
||
984 | * g_task_get_task_data: |
||
985 | * @task: a #GTask |
||
986 | * |
||
987 | * Gets @task's `task_data`. |
||
988 | * |
||
989 | * Returns: (transfer none): @task's `task_data`. |
||
990 | * |
||
991 | * Since: 2.36 |
||
992 | */ |
||
993 | gpointer |
||
994 | g_task_get_task_data (GTask *task) |
||
995 | { |
||
996 | return task->task_data; |
||
997 | } |
||
998 | |||
999 | /** |
||
1000 | * g_task_get_priority: |
||
1001 | * @task: a #GTask |
||
1002 | * |
||
1003 | * Gets @task's priority |
||
1004 | * |
||
1005 | * Returns: @task's priority |
||
1006 | * |
||
1007 | * Since: 2.36 |
||
1008 | */ |
||
1009 | gint |
||
1010 | g_task_get_priority (GTask *task) |
||
1011 | { |
||
1012 | return task->priority; |
||
1013 | } |
||
1014 | |||
1015 | /** |
||
1016 | * g_task_get_context: |
||
1017 | * @task: a #GTask |
||
1018 | * |
||
1019 | * Gets the #GMainContext that @task will return its result in (that |
||
1020 | * is, the context that was the |
||
1021 | * [thread-default main context][g-main-context-push-thread-default] |
||
1022 | * at the point when @task was created). |
||
1023 | * |
||
1024 | * This will always return a non-%NULL value, even if the task's |
||
1025 | * context is the default #GMainContext. |
||
1026 | * |
||
1027 | * Returns: (transfer none): @task's #GMainContext |
||
1028 | * |
||
1029 | * Since: 2.36 |
||
1030 | */ |
||
1031 | GMainContext * |
||
1032 | g_task_get_context (GTask *task) |
||
1033 | { |
||
1034 | return task->context; |
||
1035 | } |
||
1036 | |||
1037 | /** |
||
1038 | * g_task_get_cancellable: |
||
1039 | * @task: a #GTask |
||
1040 | * |
||
1041 | * Gets @task's #GCancellable |
||
1042 | * |
||
1043 | * Returns: (transfer none): @task's #GCancellable |
||
1044 | * |
||
1045 | * Since: 2.36 |
||
1046 | */ |
||
1047 | GCancellable * |
||
1048 | g_task_get_cancellable (GTask *task) |
||
1049 | { |
||
1050 | return task->cancellable; |
||
1051 | } |
||
1052 | |||
1053 | /** |
||
1054 | * g_task_get_check_cancellable: |
||
1055 | * @task: the #GTask |
||
1056 | * |
||
1057 | * Gets @task's check-cancellable flag. See |
||
1058 | * g_task_set_check_cancellable() for more details. |
||
1059 | * |
||
1060 | * Since: 2.36 |
||
1061 | */ |
||
1062 | gboolean |
||
1063 | g_task_get_check_cancellable (GTask *task) |
||
1064 | { |
||
1065 | return task->check_cancellable; |
||
1066 | } |
||
1067 | |||
1068 | /** |
||
1069 | * g_task_get_return_on_cancel: |
||
1070 | * @task: the #GTask |
||
1071 | * |
||
1072 | * Gets @task's return-on-cancel flag. See |
||
1073 | * g_task_set_return_on_cancel() for more details. |
||
1074 | * |
||
1075 | * Since: 2.36 |
||
1076 | */ |
||
1077 | gboolean |
||
1078 | g_task_get_return_on_cancel (GTask *task) |
||
1079 | { |
||
1080 | return task->return_on_cancel; |
||
1081 | } |
||
1082 | |||
1083 | /** |
||
1084 | * g_task_get_source_tag: |
||
1085 | * @task: a #GTask |
||
1086 | * |
||
1087 | * Gets @task's source tag. See g_task_set_source_tag(). |
||
1088 | * |
||
1089 | * Returns: (transfer none): @task's source tag |
||
1090 | * |
||
1091 | * Since: 2.36 |
||
1092 | */ |
||
1093 | gpointer |
||
1094 | g_task_get_source_tag (GTask *task) |
||
1095 | { |
||
1096 | return task->source_tag; |
||
1097 | } |
||
1098 | |||
1099 | |||
1100 | static void |
||
1101 | g_task_return_now (GTask *task) |
||
1102 | { |
||
1103 | g_main_context_push_thread_default (task->context); |
||
1104 | |||
1105 | if (task->callback != NULL) |
||
1106 | { |
||
1107 | task->callback (task->source_object, |
||
1108 | G_ASYNC_RESULT (task), |
||
1109 | task->callback_data); |
||
1110 | } |
||
1111 | |||
1112 | task->completed = TRUE; |
||
1113 | g_object_notify (G_OBJECT (task), "completed"); |
||
1114 | |||
1115 | g_main_context_pop_thread_default (task->context); |
||
1116 | } |
||
1117 | |||
1118 | static gboolean |
||
1119 | complete_in_idle_cb (gpointer task) |
||
1120 | { |
||
1121 | g_task_return_now (task); |
||
1122 | g_object_unref (task); |
||
1123 | return FALSE; |
||
1124 | } |
||
1125 | |||
1126 | typedef enum { |
||
1127 | G_TASK_RETURN_SUCCESS, |
||
1128 | G_TASK_RETURN_ERROR, |
||
1129 | G_TASK_RETURN_FROM_THREAD |
||
1130 | } GTaskReturnType; |
||
1131 | |||
1132 | static void |
||
1133 | g_task_return (GTask *task, |
||
1134 | GTaskReturnType type) |
||
1135 | { |
||
1136 | GSource *source; |
||
1137 | |||
1138 | if (type == G_TASK_RETURN_SUCCESS) |
||
1139 | task->result_set = TRUE; |
||
1140 | |||
1141 | if (task->synchronous) |
||
1142 | return; |
||
1143 | |||
1144 | /* Normally we want to invoke the task's callback when its return |
||
1145 | * value is set. But if the task is running in a thread, then we |
||
1146 | * want to wait until after the task_func returns, to simplify |
||
1147 | * locking/refcounting/etc. |
||
1148 | */ |
||
1149 | if (G_TASK_IS_THREADED (task) && type != G_TASK_RETURN_FROM_THREAD) |
||
1150 | return; |
||
1151 | |||
1152 | g_object_ref (task); |
||
1153 | |||
1154 | /* See if we can complete the task immediately. First, we have to be |
||
1155 | * running inside the task's thread/GMainContext. |
||
1156 | */ |
||
1157 | source = g_main_current_source (); |
||
1158 | if (source && g_source_get_context (source) == task->context) |
||
1159 | { |
||
1160 | /* Second, we can only complete immediately if this is not the |
||
1161 | * same iteration of the main loop that the task was created in. |
||
1162 | */ |
||
1163 | if (g_source_get_time (source) > task->creation_time) |
||
1164 | { |
||
1165 | g_task_return_now (task); |
||
1166 | g_object_unref (task); |
||
1167 | return; |
||
1168 | } |
||
1169 | } |
||
1170 | |||
1171 | /* Otherwise, complete in the next iteration */ |
||
1172 | source = g_idle_source_new (); |
||
1173 | g_task_attach_source (task, source, complete_in_idle_cb); |
||
1174 | g_source_set_name (source, "[gio] complete_in_idle_cb"); |
||
1175 | g_source_unref (source); |
||
1176 | } |
||
1177 | |||
1178 | |||
1179 | /** |
||
1180 | * GTaskThreadFunc: |
||
1181 | * @task: the #GTask |
||
1182 | * @source_object: (type GObject): @task's source object |
||
1183 | * @task_data: @task's task data |
||
1184 | * @cancellable: @task's #GCancellable, or %NULL |
||
1185 | * |
||
1186 | * The prototype for a task function to be run in a thread via |
||
1187 | * g_task_run_in_thread() or g_task_run_in_thread_sync(). |
||
1188 | * |
||
1189 | * If the return-on-cancel flag is set on @task, and @cancellable gets |
||
1190 | * cancelled, then the #GTask will be completed immediately (as though |
||
1191 | * g_task_return_error_if_cancelled() had been called), without |
||
1192 | * waiting for the task function to complete. However, the task |
||
1193 | * function will continue running in its thread in the background. The |
||
1194 | * function therefore needs to be careful about how it uses |
||
1195 | * externally-visible state in this case. See |
||
1196 | * g_task_set_return_on_cancel() for more details. |
||
1197 | * |
||
1198 | * Other than in that case, @task will be completed when the |
||
1199 | * #GTaskThreadFunc returns, not when it calls a |
||
1200 | * `g_task_return_` function. |
||
1201 | * |
||
1202 | * Since: 2.36 |
||
1203 | */ |
||
1204 | |||
1205 | static void task_thread_cancelled (GCancellable *cancellable, |
||
1206 | gpointer user_data); |
||
1207 | |||
1208 | static void |
||
1209 | g_task_thread_complete (GTask *task) |
||
1210 | { |
||
1211 | g_mutex_lock (&task->lock); |
||
1212 | if (task->thread_complete) |
||
1213 | { |
||
1214 | /* The task belatedly completed after having been cancelled |
||
1215 | * (or was cancelled in the midst of being completed). |
||
1216 | */ |
||
1217 | g_mutex_unlock (&task->lock); |
||
1218 | return; |
||
1219 | } |
||
1220 | |||
1221 | task->thread_complete = TRUE; |
||
1222 | g_mutex_unlock (&task->lock); |
||
1223 | |||
1224 | if (task->cancellable) |
||
1225 | g_signal_handlers_disconnect_by_func (task->cancellable, task_thread_cancelled, task); |
||
1226 | |||
1227 | if (task->synchronous) |
||
1228 | g_cond_signal (&task->cond); |
||
1229 | else |
||
1230 | g_task_return (task, G_TASK_RETURN_FROM_THREAD); |
||
1231 | } |
||
1232 | |||
1233 | static gboolean |
||
1234 | task_pool_manager_timeout (gpointer user_data) |
||
1235 | { |
||
1236 | g_mutex_lock (&task_pool_mutex); |
||
1237 | g_thread_pool_set_max_threads (task_pool, tasks_running + 1, NULL); |
||
1238 | g_source_set_ready_time (task_pool_manager, -1); |
||
1239 | g_mutex_unlock (&task_pool_mutex); |
||
1240 | |||
1241 | return TRUE; |
||
1242 | } |
||
1243 | |||
1244 | static void |
||
1245 | g_task_thread_setup (void) |
||
1246 | { |
||
1247 | g_private_set (&task_private, GUINT_TO_POINTER (TRUE)); |
||
1248 | g_mutex_lock (&task_pool_mutex); |
||
1249 | tasks_running++; |
||
1250 | |||
1251 | if (tasks_running == G_TASK_POOL_SIZE) |
||
1252 | task_wait_time = G_TASK_WAIT_TIME_BASE; |
||
1253 | else if (tasks_running > G_TASK_POOL_SIZE && task_wait_time < G_TASK_WAIT_TIME_MAX) |
||
1254 | task_wait_time *= G_TASK_WAIT_TIME_MULTIPLIER; |
||
1255 | |||
1256 | if (tasks_running >= G_TASK_POOL_SIZE) |
||
1257 | g_source_set_ready_time (task_pool_manager, g_get_monotonic_time () + task_wait_time); |
||
1258 | |||
1259 | g_mutex_unlock (&task_pool_mutex); |
||
1260 | } |
||
1261 | |||
1262 | static void |
||
1263 | g_task_thread_cleanup (void) |
||
1264 | { |
||
1265 | gint tasks_pending; |
||
1266 | |||
1267 | g_mutex_lock (&task_pool_mutex); |
||
1268 | tasks_pending = g_thread_pool_unprocessed (task_pool); |
||
1269 | |||
1270 | if (tasks_running > G_TASK_POOL_SIZE) |
||
1271 | g_thread_pool_set_max_threads (task_pool, tasks_running - 1, NULL); |
||
1272 | else if (tasks_running + tasks_pending < G_TASK_POOL_SIZE) |
||
1273 | g_source_set_ready_time (task_pool_manager, -1); |
||
1274 | |||
1275 | tasks_running--; |
||
1276 | g_mutex_unlock (&task_pool_mutex); |
||
1277 | g_private_set (&task_private, GUINT_TO_POINTER (FALSE)); |
||
1278 | } |
||
1279 | |||
1280 | static void |
||
1281 | g_task_thread_pool_thread (gpointer thread_data, |
||
1282 | gpointer pool_data) |
||
1283 | { |
||
1284 | GTask *task = thread_data; |
||
1285 | |||
1286 | g_task_thread_setup (); |
||
1287 | |||
1288 | task->task_func (task, task->source_object, task->task_data, |
||
1289 | task->cancellable); |
||
1290 | g_task_thread_complete (task); |
||
1291 | g_object_unref (task); |
||
1292 | |||
1293 | g_task_thread_cleanup (); |
||
1294 | } |
||
1295 | |||
1296 | static void |
||
1297 | task_thread_cancelled (GCancellable *cancellable, |
||
1298 | gpointer user_data) |
||
1299 | { |
||
1300 | GTask *task = user_data; |
||
1301 | |||
1302 | /* Move this task to the front of the queue - no need for |
||
1303 | * a complete resorting of the queue. |
||
1304 | */ |
||
1305 | g_thread_pool_move_to_front (task_pool, task); |
||
1306 | |||
1307 | g_mutex_lock (&task->lock); |
||
1308 | task->thread_cancelled = TRUE; |
||
1309 | |||
1310 | if (!task->return_on_cancel) |
||
1311 | { |
||
1312 | g_mutex_unlock (&task->lock); |
||
1313 | return; |
||
1314 | } |
||
1315 | |||
1316 | /* We don't actually set task->error; g_task_return_error() doesn't |
||
1317 | * use a lock, and g_task_propagate_error() will call |
||
1318 | * g_cancellable_set_error_if_cancelled() anyway. |
||
1319 | */ |
||
1320 | g_mutex_unlock (&task->lock); |
||
1321 | g_task_thread_complete (task); |
||
1322 | } |
||
1323 | |||
1324 | static void |
||
1325 | task_thread_cancelled_disconnect_notify (gpointer task, |
||
1326 | GClosure *closure) |
||
1327 | { |
||
1328 | g_object_unref (task); |
||
1329 | } |
||
1330 | |||
1331 | static void |
||
1332 | g_task_start_task_thread (GTask *task, |
||
1333 | GTaskThreadFunc task_func) |
||
1334 | { |
||
1335 | g_mutex_init (&task->lock); |
||
1336 | g_cond_init (&task->cond); |
||
1337 | |||
1338 | g_mutex_lock (&task->lock); |
||
1339 | |||
1340 | task->task_func = task_func; |
||
1341 | |||
1342 | if (task->cancellable) |
||
1343 | { |
||
1344 | if (task->return_on_cancel && |
||
1345 | g_cancellable_set_error_if_cancelled (task->cancellable, |
||
1346 | &task->error)) |
||
1347 | { |
||
1348 | task->thread_cancelled = task->thread_complete = TRUE; |
||
1349 | g_thread_pool_push (task_pool, g_object_ref (task), NULL); |
||
1350 | return; |
||
1351 | } |
||
1352 | |||
1353 | /* This introduces a reference count loop between the GTask and |
||
1354 | * GCancellable, but is necessary to avoid a race on finalising the GTask |
||
1355 | * between task_thread_cancelled() (in one thread) and |
||
1356 | * g_task_thread_complete() (in another). |
||
1357 | * |
||
1358 | * Accordingly, the signal handler *must* be removed once the task has |
||
1359 | * completed. |
||
1360 | */ |
||
1361 | g_signal_connect_data (task->cancellable, "cancelled", |
||
1362 | G_CALLBACK (task_thread_cancelled), |
||
1363 | g_object_ref (task), |
||
1364 | task_thread_cancelled_disconnect_notify, 0); |
||
1365 | } |
||
1366 | |||
1367 | if (g_private_get (&task_private)) |
||
1368 | task->blocking_other_task = TRUE; |
||
1369 | g_thread_pool_push (task_pool, g_object_ref (task), NULL); |
||
1370 | } |
||
1371 | |||
1372 | /** |
||
1373 | * g_task_run_in_thread: |
||
1374 | * @task: a #GTask |
||
1375 | * @task_func: a #GTaskThreadFunc |
||
1376 | * |
||
1377 | * Runs @task_func in another thread. When @task_func returns, @task's |
||
1378 | * #GAsyncReadyCallback will be invoked in @task's #GMainContext. |
||
1379 | * |
||
1380 | * This takes a ref on @task until the task completes. |
||
1381 | * |
||
1382 | * See #GTaskThreadFunc for more details about how @task_func is handled. |
||
1383 | * |
||
1384 | * Although GLib currently rate-limits the tasks queued via |
||
1385 | * g_task_run_in_thread(), you should not assume that it will always |
||
1386 | * do this. If you have a very large number of tasks to run, but don't |
||
1387 | * want them to all run at once, you should only queue a limited |
||
1388 | * number of them at a time. |
||
1389 | * |
||
1390 | * Since: 2.36 |
||
1391 | */ |
||
1392 | void |
||
1393 | g_task_run_in_thread (GTask *task, |
||
1394 | GTaskThreadFunc task_func) |
||
1395 | { |
||
1396 | g_return_if_fail (G_IS_TASK (task)); |
||
1397 | |||
1398 | g_object_ref (task); |
||
1399 | g_task_start_task_thread (task, task_func); |
||
1400 | |||
1401 | /* The task may already be cancelled, or g_thread_pool_push() may |
||
1402 | * have failed. |
||
1403 | */ |
||
1404 | if (task->thread_complete) |
||
1405 | { |
||
1406 | g_mutex_unlock (&task->lock); |
||
1407 | g_task_return (task, G_TASK_RETURN_FROM_THREAD); |
||
1408 | } |
||
1409 | else |
||
1410 | g_mutex_unlock (&task->lock); |
||
1411 | |||
1412 | g_object_unref (task); |
||
1413 | } |
||
1414 | |||
1415 | /** |
||
1416 | * g_task_run_in_thread_sync: |
||
1417 | * @task: a #GTask |
||
1418 | * @task_func: a #GTaskThreadFunc |
||
1419 | * |
||
1420 | * Runs @task_func in another thread, and waits for it to return or be |
||
1421 | * cancelled. You can use g_task_propagate_pointer(), etc, afterward |
||
1422 | * to get the result of @task_func. |
||
1423 | * |
||
1424 | * See #GTaskThreadFunc for more details about how @task_func is handled. |
||
1425 | * |
||
1426 | * Normally this is used with tasks created with a %NULL |
||
1427 | * `callback`, but note that even if the task does |
||
1428 | * have a callback, it will not be invoked when @task_func returns. |
||
1429 | * #GTask:completed will be set to %TRUE just before this function returns. |
||
1430 | * |
||
1431 | * Although GLib currently rate-limits the tasks queued via |
||
1432 | * g_task_run_in_thread_sync(), you should not assume that it will |
||
1433 | * always do this. If you have a very large number of tasks to run, |
||
1434 | * but don't want them to all run at once, you should only queue a |
||
1435 | * limited number of them at a time. |
||
1436 | * |
||
1437 | * Since: 2.36 |
||
1438 | */ |
||
1439 | void |
||
1440 | g_task_run_in_thread_sync (GTask *task, |
||
1441 | GTaskThreadFunc task_func) |
||
1442 | { |
||
1443 | g_return_if_fail (G_IS_TASK (task)); |
||
1444 | |||
1445 | g_object_ref (task); |
||
1446 | |||
1447 | task->synchronous = TRUE; |
||
1448 | g_task_start_task_thread (task, task_func); |
||
1449 | |||
1450 | while (!task->thread_complete) |
||
1451 | g_cond_wait (&task->cond, &task->lock); |
||
1452 | |||
1453 | g_mutex_unlock (&task->lock); |
||
1454 | |||
1455 | /* Notify of completion in this thread. */ |
||
1456 | task->completed = TRUE; |
||
1457 | g_object_notify (G_OBJECT (task), "completed"); |
||
1458 | |||
1459 | g_object_unref (task); |
||
1460 | } |
||
1461 | |||
1462 | /** |
||
1463 | * g_task_attach_source: |
||
1464 | * @task: a #GTask |
||
1465 | * @source: the source to attach |
||
1466 | * @callback: the callback to invoke when @source triggers |
||
1467 | * |
||
1468 | * A utility function for dealing with async operations where you need |
||
1469 | * to wait for a #GSource to trigger. Attaches @source to @task's |
||
1470 | * #GMainContext with @task's [priority][io-priority], and sets @source's |
||
1471 | * callback to @callback, with @task as the callback's `user_data`. |
||
1472 | * |
||
1473 | * This takes a reference on @task until @source is destroyed. |
||
1474 | * |
||
1475 | * Since: 2.36 |
||
1476 | */ |
||
1477 | void |
||
1478 | g_task_attach_source (GTask *task, |
||
1479 | GSource *source, |
||
1480 | GSourceFunc callback) |
||
1481 | { |
||
1482 | g_source_set_callback (source, callback, |
||
1483 | g_object_ref (task), g_object_unref); |
||
1484 | g_source_set_priority (source, task->priority); |
||
1485 | g_source_attach (source, task->context); |
||
1486 | } |
||
1487 | |||
1488 | |||
1489 | static gboolean |
||
1490 | g_task_propagate_error (GTask *task, |
||
1491 | GError **error) |
||
1492 | { |
||
1493 | if (task->check_cancellable && |
||
1494 | g_cancellable_set_error_if_cancelled (task->cancellable, error)) |
||
1495 | return TRUE; |
||
1496 | else if (task->error) |
||
1497 | { |
||
1498 | g_propagate_error (error, task->error); |
||
1499 | task->error = NULL; |
||
1500 | return TRUE; |
||
1501 | } |
||
1502 | else |
||
1503 | return FALSE; |
||
1504 | } |
||
1505 | |||
1506 | /** |
||
1507 | * g_task_return_pointer: |
||
1508 | * @task: a #GTask |
||
1509 | * @result: (allow-none) (transfer full): the pointer result of a task |
||
1510 | * function |
||
1511 | * @result_destroy: (allow-none): a #GDestroyNotify function. |
||
1512 | * |
||
1513 | * Sets @task's result to @result and completes the task. If @result |
||
1514 | * is not %NULL, then @result_destroy will be used to free @result if |
||
1515 | * the caller does not take ownership of it with |
||
1516 | * g_task_propagate_pointer(). |
||
1517 | * |
||
1518 | * "Completes the task" means that for an ordinary asynchronous task |
||
1519 | * it will either invoke the task's callback, or else queue that |
||
1520 | * callback to be invoked in the proper #GMainContext, or in the next |
||
1521 | * iteration of the current #GMainContext. For a task run via |
||
1522 | * g_task_run_in_thread() or g_task_run_in_thread_sync(), calling this |
||
1523 | * method will save @result to be returned to the caller later, but |
||
1524 | * the task will not actually be completed until the #GTaskThreadFunc |
||
1525 | * exits. |
||
1526 | * |
||
1527 | * Note that since the task may be completed before returning from |
||
1528 | * g_task_return_pointer(), you cannot assume that @result is still |
||
1529 | * valid after calling this, unless you are still holding another |
||
1530 | * reference on it. |
||
1531 | * |
||
1532 | * Since: 2.36 |
||
1533 | */ |
||
1534 | void |
||
1535 | g_task_return_pointer (GTask *task, |
||
1536 | gpointer result, |
||
1537 | GDestroyNotify result_destroy) |
||
1538 | { |
||
1539 | g_return_if_fail (task->result_set == FALSE); |
||
1540 | |||
1541 | task->result.pointer = result; |
||
1542 | task->result_destroy = result_destroy; |
||
1543 | |||
1544 | g_task_return (task, G_TASK_RETURN_SUCCESS); |
||
1545 | } |
||
1546 | |||
1547 | /** |
||
1548 | * g_task_propagate_pointer: |
||
1549 | * @task: a #GTask |
||
1550 | * @error: return location for a #GError |
||
1551 | * |
||
1552 | * Gets the result of @task as a pointer, and transfers ownership |
||
1553 | * of that value to the caller. |
||
1554 | * |
||
1555 | * If the task resulted in an error, or was cancelled, then this will |
||
1556 | * instead return %NULL and set @error. |
||
1557 | * |
||
1558 | * Since this method transfers ownership of the return value (or |
||
1559 | * error) to the caller, you may only call it once. |
||
1560 | * |
||
1561 | * Returns: (transfer full): the task result, or %NULL on error |
||
1562 | * |
||
1563 | * Since: 2.36 |
||
1564 | */ |
||
1565 | gpointer |
||
1566 | g_task_propagate_pointer (GTask *task, |
||
1567 | GError **error) |
||
1568 | { |
||
1569 | if (g_task_propagate_error (task, error)) |
||
1570 | return NULL; |
||
1571 | |||
1572 | g_return_val_if_fail (task->result_set == TRUE, NULL); |
||
1573 | |||
1574 | task->result_destroy = NULL; |
||
1575 | task->result_set = FALSE; |
||
1576 | return task->result.pointer; |
||
1577 | } |
||
1578 | |||
1579 | /** |
||
1580 | * g_task_return_int: |
||
1581 | * @task: a #GTask. |
||
1582 | * @result: the integer (#gssize) result of a task function. |
||
1583 | * |
||
1584 | * Sets @task's result to @result and completes the task (see |
||
1585 | * g_task_return_pointer() for more discussion of exactly what this |
||
1586 | * means). |
||
1587 | * |
||
1588 | * Since: 2.36 |
||
1589 | */ |
||
1590 | void |
||
1591 | g_task_return_int (GTask *task, |
||
1592 | gssize result) |
||
1593 | { |
||
1594 | g_return_if_fail (task->result_set == FALSE); |
||
1595 | |||
1596 | task->result.size = result; |
||
1597 | |||
1598 | g_task_return (task, G_TASK_RETURN_SUCCESS); |
||
1599 | } |
||
1600 | |||
1601 | /** |
||
1602 | * g_task_propagate_int: |
||
1603 | * @task: a #GTask. |
||
1604 | * @error: return location for a #GError |
||
1605 | * |
||
1606 | * Gets the result of @task as an integer (#gssize). |
||
1607 | * |
||
1608 | * If the task resulted in an error, or was cancelled, then this will |
||
1609 | * instead return -1 and set @error. |
||
1610 | * |
||
1611 | * Since this method transfers ownership of the return value (or |
||
1612 | * error) to the caller, you may only call it once. |
||
1613 | * |
||
1614 | * Returns: the task result, or -1 on error |
||
1615 | * |
||
1616 | * Since: 2.36 |
||
1617 | */ |
||
1618 | gssize |
||
1619 | g_task_propagate_int (GTask *task, |
||
1620 | GError **error) |
||
1621 | { |
||
1622 | if (g_task_propagate_error (task, error)) |
||
1623 | return -1; |
||
1624 | |||
1625 | g_return_val_if_fail (task->result_set == TRUE, -1); |
||
1626 | |||
1627 | task->result_set = FALSE; |
||
1628 | return task->result.size; |
||
1629 | } |
||
1630 | |||
1631 | /** |
||
1632 | * g_task_return_boolean: |
||
1633 | * @task: a #GTask. |
||
1634 | * @result: the #gboolean result of a task function. |
||
1635 | * |
||
1636 | * Sets @task's result to @result and completes the task (see |
||
1637 | * g_task_return_pointer() for more discussion of exactly what this |
||
1638 | * means). |
||
1639 | * |
||
1640 | * Since: 2.36 |
||
1641 | */ |
||
1642 | void |
||
1643 | g_task_return_boolean (GTask *task, |
||
1644 | gboolean result) |
||
1645 | { |
||
1646 | g_return_if_fail (task->result_set == FALSE); |
||
1647 | |||
1648 | task->result.boolean = result; |
||
1649 | |||
1650 | g_task_return (task, G_TASK_RETURN_SUCCESS); |
||
1651 | } |
||
1652 | |||
1653 | /** |
||
1654 | * g_task_propagate_boolean: |
||
1655 | * @task: a #GTask. |
||
1656 | * @error: return location for a #GError |
||
1657 | * |
||
1658 | * Gets the result of @task as a #gboolean. |
||
1659 | * |
||
1660 | * If the task resulted in an error, or was cancelled, then this will |
||
1661 | * instead return %FALSE and set @error. |
||
1662 | * |
||
1663 | * Since this method transfers ownership of the return value (or |
||
1664 | * error) to the caller, you may only call it once. |
||
1665 | * |
||
1666 | * Returns: the task result, or %FALSE on error |
||
1667 | * |
||
1668 | * Since: 2.36 |
||
1669 | */ |
||
1670 | gboolean |
||
1671 | g_task_propagate_boolean (GTask *task, |
||
1672 | GError **error) |
||
1673 | { |
||
1674 | if (g_task_propagate_error (task, error)) |
||
1675 | return FALSE; |
||
1676 | |||
1677 | g_return_val_if_fail (task->result_set == TRUE, FALSE); |
||
1678 | |||
1679 | task->result_set = FALSE; |
||
1680 | return task->result.boolean; |
||
1681 | } |
||
1682 | |||
1683 | /** |
||
1684 | * g_task_return_error: |
||
1685 | * @task: a #GTask. |
||
1686 | * @error: (transfer full): the #GError result of a task function. |
||
1687 | * |
||
1688 | * Sets @task's result to @error (which @task assumes ownership of) |
||
1689 | * and completes the task (see g_task_return_pointer() for more |
||
1690 | * discussion of exactly what this means). |
||
1691 | * |
||
1692 | * Note that since the task takes ownership of @error, and since the |
||
1693 | * task may be completed before returning from g_task_return_error(), |
||
1694 | * you cannot assume that @error is still valid after calling this. |
||
1695 | * Call g_error_copy() on the error if you need to keep a local copy |
||
1696 | * as well. |
||
1697 | * |
||
1698 | * See also g_task_return_new_error(). |
||
1699 | * |
||
1700 | * Since: 2.36 |
||
1701 | */ |
||
1702 | void |
||
1703 | g_task_return_error (GTask *task, |
||
1704 | GError *error) |
||
1705 | { |
||
1706 | g_return_if_fail (task->result_set == FALSE); |
||
1707 | g_return_if_fail (error != NULL); |
||
1708 | |||
1709 | task->error = error; |
||
1710 | |||
1711 | g_task_return (task, G_TASK_RETURN_ERROR); |
||
1712 | } |
||
1713 | |||
1714 | /** |
||
1715 | * g_task_return_new_error: |
||
1716 | * @task: a #GTask. |
||
1717 | * @domain: a #GQuark. |
||
1718 | * @code: an error code. |
||
1719 | * @format: a string with format characters. |
||
1720 | * @...: a list of values to insert into @format. |
||
1721 | * |
||
1722 | * Sets @task's result to a new #GError created from @domain, @code, |
||
1723 | * @format, and the remaining arguments, and completes the task (see |
||
1724 | * g_task_return_pointer() for more discussion of exactly what this |
||
1725 | * means). |
||
1726 | * |
||
1727 | * See also g_task_return_error(). |
||
1728 | * |
||
1729 | * Since: 2.36 |
||
1730 | */ |
||
1731 | void |
||
1732 | g_task_return_new_error (GTask *task, |
||
1733 | GQuark domain, |
||
1734 | gint code, |
||
1735 | const char *format, |
||
1736 | ...) |
||
1737 | { |
||
1738 | GError *error; |
||
1739 | va_list args; |
||
1740 | |||
1741 | va_start (args, format); |
||
1742 | error = g_error_new_valist (domain, code, format, args); |
||
1743 | va_end (args); |
||
1744 | |||
1745 | g_task_return_error (task, error); |
||
1746 | } |
||
1747 | |||
1748 | /** |
||
1749 | * g_task_return_error_if_cancelled: |
||
1750 | * @task: a #GTask |
||
1751 | * |
||
1752 | * Checks if @task's #GCancellable has been cancelled, and if so, sets |
||
1753 | * @task's error accordingly and completes the task (see |
||
1754 | * g_task_return_pointer() for more discussion of exactly what this |
||
1755 | * means). |
||
1756 | * |
||
1757 | * Returns: %TRUE if @task has been cancelled, %FALSE if not |
||
1758 | * |
||
1759 | * Since: 2.36 |
||
1760 | */ |
||
1761 | gboolean |
||
1762 | g_task_return_error_if_cancelled (GTask *task) |
||
1763 | { |
||
1764 | GError *error = NULL; |
||
1765 | |||
1766 | g_return_val_if_fail (task->result_set == FALSE, FALSE); |
||
1767 | |||
1768 | if (g_cancellable_set_error_if_cancelled (task->cancellable, &error)) |
||
1769 | { |
||
1770 | /* We explicitly set task->error so this works even when |
||
1771 | * check-cancellable is not set. |
||
1772 | */ |
||
1773 | g_clear_error (&task->error); |
||
1774 | task->error = error; |
||
1775 | |||
1776 | g_task_return (task, G_TASK_RETURN_ERROR); |
||
1777 | return TRUE; |
||
1778 | } |
||
1779 | else |
||
1780 | return FALSE; |
||
1781 | } |
||
1782 | |||
1783 | /** |
||
1784 | * g_task_had_error: |
||
1785 | * @task: a #GTask. |
||
1786 | * |
||
1787 | * Tests if @task resulted in an error. |
||
1788 | * |
||
1789 | * Returns: %TRUE if the task resulted in an error, %FALSE otherwise. |
||
1790 | * |
||
1791 | * Since: 2.36 |
||
1792 | */ |
||
1793 | gboolean |
||
1794 | g_task_had_error (GTask *task) |
||
1795 | { |
||
1796 | if (task->error != NULL) |
||
1797 | return TRUE; |
||
1798 | |||
1799 | if (task->check_cancellable && g_cancellable_is_cancelled (task->cancellable)) |
||
1800 | return TRUE; |
||
1801 | |||
1802 | return FALSE; |
||
1803 | } |
||
1804 | |||
1805 | /** |
||
1806 | * g_task_get_completed: |
||
1807 | * @task: a #GTask. |
||
1808 | * |
||
1809 | * Gets the value of #GTask:completed. This changes from %FALSE to %TRUE after |
||
1810 | * the task’s callback is invoked, and will return %FALSE if called from inside |
||
1811 | * the callback. |
||
1812 | * |
||
1813 | * Returns: %TRUE if the task has completed, %FALSE otherwise. |
||
1814 | * |
||
1815 | * Since: 2.44 |
||
1816 | */ |
||
1817 | gboolean |
||
1818 | g_task_get_completed (GTask *task) |
||
1819 | { |
||
1820 | g_return_val_if_fail (G_IS_TASK (task), FALSE); |
||
1821 | |||
1822 | return task->completed; |
||
1823 | } |
||
1824 | |||
1825 | /** |
||
1826 | * g_task_is_valid: |
||
1827 | * @result: (type Gio.AsyncResult): A #GAsyncResult |
||
1828 | * @source_object: (allow-none) (type GObject): the source object |
||
1829 | * expected to be associated with the task |
||
1830 | * |
||
1831 | * Checks that @result is a #GTask, and that @source_object is its |
||
1832 | * source object (or that @source_object is %NULL and @result has no |
||
1833 | * source object). This can be used in g_return_if_fail() checks. |
||
1834 | * |
||
1835 | * Returns: %TRUE if @result and @source_object are valid, %FALSE |
||
1836 | * if not |
||
1837 | * |
||
1838 | * Since: 2.36 |
||
1839 | */ |
||
1840 | gboolean |
||
1841 | g_task_is_valid (gpointer result, |
||
1842 | gpointer source_object) |
||
1843 | { |
||
1844 | if (!G_IS_TASK (result)) |
||
1845 | return FALSE; |
||
1846 | |||
1847 | return G_TASK (result)->source_object == source_object; |
||
1848 | } |
||
1849 | |||
1850 | static gint |
||
1851 | g_task_compare_priority (gconstpointer a, |
||
1852 | gconstpointer b, |
||
1853 | gpointer user_data) |
||
1854 | { |
||
1855 | const GTask *ta = a; |
||
1856 | const GTask *tb = b; |
||
1857 | gboolean a_cancelled, b_cancelled; |
||
1858 | |||
1859 | /* Tasks that are causing other tasks to block have higher |
||
1860 | * priority. |
||
1861 | */ |
||
1862 | if (ta->blocking_other_task && !tb->blocking_other_task) |
||
1863 | return -1; |
||
1864 | else if (tb->blocking_other_task && !ta->blocking_other_task) |
||
1865 | return 1; |
||
1866 | |||
1867 | /* Let already-cancelled tasks finish right away */ |
||
1868 | a_cancelled = (ta->check_cancellable && |
||
1869 | g_cancellable_is_cancelled (ta->cancellable)); |
||
1870 | b_cancelled = (tb->check_cancellable && |
||
1871 | g_cancellable_is_cancelled (tb->cancellable)); |
||
1872 | if (a_cancelled && !b_cancelled) |
||
1873 | return -1; |
||
1874 | else if (b_cancelled && !a_cancelled) |
||
1875 | return 1; |
||
1876 | |||
1877 | /* Lower priority == run sooner == negative return value */ |
||
1878 | return ta->priority - tb->priority; |
||
1879 | } |
||
1880 | |||
1881 | static gboolean |
||
1882 | trivial_source_dispatch (GSource *source, |
||
1883 | GSourceFunc callback, |
||
1884 | gpointer user_data) |
||
1885 | { |
||
1886 | return callback (user_data); |
||
1887 | } |
||
1888 | |||
1889 | GSourceFuncs trivial_source_funcs = { |
||
1890 | NULL, /* prepare */ |
||
1891 | NULL, /* check */ |
||
1892 | trivial_source_dispatch, |
||
1893 | NULL |
||
1894 | }; |
||
1895 | |||
1896 | static void |
||
1897 | g_task_thread_pool_init (void) |
||
1898 | { |
||
1899 | task_pool = g_thread_pool_new (g_task_thread_pool_thread, NULL, |
||
1900 | G_TASK_POOL_SIZE, FALSE, NULL); |
||
1901 | g_assert (task_pool != NULL); |
||
1902 | |||
1903 | g_thread_pool_set_sort_function (task_pool, g_task_compare_priority, NULL); |
||
1904 | |||
1905 | task_pool_manager = g_source_new (&trivial_source_funcs, sizeof (GSource)); |
||
1906 | g_source_set_callback (task_pool_manager, task_pool_manager_timeout, NULL, NULL); |
||
1907 | g_source_set_ready_time (task_pool_manager, -1); |
||
1908 | g_source_attach (task_pool_manager, |
||
1909 | GLIB_PRIVATE_CALL (g_get_worker_context ())); |
||
1910 | g_source_unref (task_pool_manager); |
||
1911 | } |
||
1912 | |||
1913 | static void |
||
1914 | g_task_get_property (GObject *object, |
||
1915 | guint prop_id, |
||
1916 | GValue *value, |
||
1917 | GParamSpec *pspec) |
||
1918 | { |
||
1919 | GTask *task = G_TASK (object); |
||
1920 | |||
1921 | switch ((GTaskProperty) prop_id) |
||
1922 | { |
||
1923 | case PROP_COMPLETED: |
||
1924 | g_value_set_boolean (value, task->completed); |
||
1925 | break; |
||
1926 | } |
||
1927 | } |
||
1928 | |||
1929 | static void |
||
1930 | g_task_class_init (GTaskClass *klass) |
||
1931 | { |
||
1932 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
||
1933 | |||
1934 | gobject_class->get_property = g_task_get_property; |
||
1935 | gobject_class->finalize = g_task_finalize; |
||
1936 | |||
1937 | /** |
||
1938 | * GTask:completed: |
||
1939 | * |
||
1940 | * Whether the task has completed, meaning its callback (if set) has been |
||
1941 | * invoked. This can only happen after g_task_return_pointer(), |
||
1942 | * g_task_return_error() or one of the other return functions have been called |
||
1943 | * on the task. |
||
1944 | * |
||
1945 | * This property is guaranteed to change from %FALSE to %TRUE exactly once. |
||
1946 | * |
||
1947 | * The #GObject::notify signal for this change is emitted in the same main |
||
1948 | * context as the task’s callback, immediately after that callback is invoked. |
||
1949 | * |
||
1950 | * Since: 2.44 |
||
1951 | */ |
||
1952 | g_object_class_install_property (gobject_class, PROP_COMPLETED, |
||
1953 | g_param_spec_boolean ("completed", |
||
1954 | P_("Task completed"), |
||
1955 | P_("Whether the task has completed yet"), |
||
1956 | FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); |
||
1957 | } |
||
1958 | |||
1959 | static gpointer |
||
1960 | g_task_get_user_data (GAsyncResult *res) |
||
1961 | { |
||
1962 | return G_TASK (res)->callback_data; |
||
1963 | } |
||
1964 | |||
1965 | static gboolean |
||
1966 | g_task_is_tagged (GAsyncResult *res, |
||
1967 | gpointer source_tag) |
||
1968 | { |
||
1969 | return G_TASK (res)->source_tag == source_tag; |
||
1970 | } |
||
1971 | |||
1972 | static void |
||
1973 | g_task_async_result_iface_init (GAsyncResultIface *iface) |
||
1974 | { |
||
1975 | iface->get_user_data = g_task_get_user_data; |
||
1976 | iface->get_source_object = g_task_ref_source_object; |
||
1977 | iface->is_tagged = g_task_is_tagged; |
||
1978 | } |