/src/glib/gio/gasyncinitable.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* GIO - GLib Input, Output and Streaming Library |
2 | | * |
3 | | * Copyright (C) 2009 Red Hat, Inc. |
4 | | * |
5 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General |
18 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
19 | | * |
20 | | * Author: Alexander Larsson <alexl@redhat.com> |
21 | | */ |
22 | | |
23 | | #include "config.h" |
24 | | #include "gasyncinitable.h" |
25 | | #include "gasyncresult.h" |
26 | | #include "gsimpleasyncresult.h" |
27 | | #include "gtask.h" |
28 | | #include "glibintl.h" |
29 | | |
30 | | |
31 | | /** |
32 | | * SECTION:gasyncinitable |
33 | | * @short_description: Asynchronously failable object initialization interface |
34 | | * @include: gio/gio.h |
35 | | * @see_also: #GInitable |
36 | | * |
37 | | * This is the asynchronous version of #GInitable; it behaves the same |
38 | | * in all ways except that initialization is asynchronous. For more details |
39 | | * see the descriptions on #GInitable. |
40 | | * |
41 | | * A class may implement both the #GInitable and #GAsyncInitable interfaces. |
42 | | * |
43 | | * Users of objects implementing this are not intended to use the interface |
44 | | * method directly; instead it will be used automatically in various ways. |
45 | | * For C applications you generally just call g_async_initable_new_async() |
46 | | * directly, or indirectly via a foo_thing_new_async() wrapper. This will call |
47 | | * g_async_initable_init_async() under the cover, calling back with %NULL and |
48 | | * a set %GError on failure. |
49 | | * |
50 | | * A typical implementation might look something like this: |
51 | | * |
52 | | * |[<!-- language="C" --> |
53 | | * enum { |
54 | | * NOT_INITIALIZED, |
55 | | * INITIALIZING, |
56 | | * INITIALIZED |
57 | | * }; |
58 | | * |
59 | | * static void |
60 | | * _foo_ready_cb (Foo *self) |
61 | | * { |
62 | | * GList *l; |
63 | | * |
64 | | * self->priv->state = INITIALIZED; |
65 | | * |
66 | | * for (l = self->priv->init_results; l != NULL; l = l->next) |
67 | | * { |
68 | | * GTask *task = l->data; |
69 | | * |
70 | | * if (self->priv->success) |
71 | | * g_task_return_boolean (task, TRUE); |
72 | | * else |
73 | | * g_task_return_new_error (task, ...); |
74 | | * g_object_unref (task); |
75 | | * } |
76 | | * |
77 | | * g_list_free (self->priv->init_results); |
78 | | * self->priv->init_results = NULL; |
79 | | * } |
80 | | * |
81 | | * static void |
82 | | * foo_init_async (GAsyncInitable *initable, |
83 | | * int io_priority, |
84 | | * GCancellable *cancellable, |
85 | | * GAsyncReadyCallback callback, |
86 | | * gpointer user_data) |
87 | | * { |
88 | | * Foo *self = FOO (initable); |
89 | | * GTask *task; |
90 | | * |
91 | | * task = g_task_new (initable, cancellable, callback, user_data); |
92 | | * g_task_set_name (task, G_STRFUNC); |
93 | | * |
94 | | * switch (self->priv->state) |
95 | | * { |
96 | | * case NOT_INITIALIZED: |
97 | | * _foo_get_ready (self); |
98 | | * self->priv->init_results = g_list_append (self->priv->init_results, |
99 | | * task); |
100 | | * self->priv->state = INITIALIZING; |
101 | | * break; |
102 | | * case INITIALIZING: |
103 | | * self->priv->init_results = g_list_append (self->priv->init_results, |
104 | | * task); |
105 | | * break; |
106 | | * case INITIALIZED: |
107 | | * if (!self->priv->success) |
108 | | * g_task_return_new_error (task, ...); |
109 | | * else |
110 | | * g_task_return_boolean (task, TRUE); |
111 | | * g_object_unref (task); |
112 | | * break; |
113 | | * } |
114 | | * } |
115 | | * |
116 | | * static gboolean |
117 | | * foo_init_finish (GAsyncInitable *initable, |
118 | | * GAsyncResult *result, |
119 | | * GError **error) |
120 | | * { |
121 | | * g_return_val_if_fail (g_task_is_valid (result, initable), FALSE); |
122 | | * |
123 | | * return g_task_propagate_boolean (G_TASK (result), error); |
124 | | * } |
125 | | * |
126 | | * static void |
127 | | * foo_async_initable_iface_init (gpointer g_iface, |
128 | | * gpointer data) |
129 | | * { |
130 | | * GAsyncInitableIface *iface = g_iface; |
131 | | * |
132 | | * iface->init_async = foo_init_async; |
133 | | * iface->init_finish = foo_init_finish; |
134 | | * } |
135 | | * ]| |
136 | | */ |
137 | | |
138 | | static void g_async_initable_real_init_async (GAsyncInitable *initable, |
139 | | int io_priority, |
140 | | GCancellable *cancellable, |
141 | | GAsyncReadyCallback callback, |
142 | | gpointer user_data); |
143 | | static gboolean g_async_initable_real_init_finish (GAsyncInitable *initable, |
144 | | GAsyncResult *res, |
145 | | GError **error); |
146 | | |
147 | | |
148 | | typedef GAsyncInitableIface GAsyncInitableInterface; |
149 | | G_DEFINE_INTERFACE (GAsyncInitable, g_async_initable, G_TYPE_OBJECT) |
150 | | |
151 | | |
152 | | static void |
153 | | g_async_initable_default_init (GAsyncInitableInterface *iface) |
154 | 0 | { |
155 | 0 | iface->init_async = g_async_initable_real_init_async; |
156 | 0 | iface->init_finish = g_async_initable_real_init_finish; |
157 | 0 | } |
158 | | |
159 | | /** |
160 | | * g_async_initable_init_async: |
161 | | * @initable: a #GAsyncInitable. |
162 | | * @io_priority: the [I/O priority][io-priority] of the operation |
163 | | * @cancellable: optional #GCancellable object, %NULL to ignore. |
164 | | * @callback: a #GAsyncReadyCallback to call when the request is satisfied |
165 | | * @user_data: the data to pass to callback function |
166 | | * |
167 | | * Starts asynchronous initialization of the object implementing the |
168 | | * interface. This must be done before any real use of the object after |
169 | | * initial construction. If the object also implements #GInitable you can |
170 | | * optionally call g_initable_init() instead. |
171 | | * |
172 | | * This method is intended for language bindings. If writing in C, |
173 | | * g_async_initable_new_async() should typically be used instead. |
174 | | * |
175 | | * When the initialization is finished, @callback will be called. You can |
176 | | * then call g_async_initable_init_finish() to get the result of the |
177 | | * initialization. |
178 | | * |
179 | | * Implementations may also support cancellation. If @cancellable is not |
180 | | * %NULL, then initialization can be cancelled by triggering the cancellable |
181 | | * object from another thread. If the operation was cancelled, the error |
182 | | * %G_IO_ERROR_CANCELLED will be returned. If @cancellable is not %NULL, and |
183 | | * the object doesn't support cancellable initialization, the error |
184 | | * %G_IO_ERROR_NOT_SUPPORTED will be returned. |
185 | | * |
186 | | * As with #GInitable, if the object is not initialized, or initialization |
187 | | * returns with an error, then all operations on the object except |
188 | | * g_object_ref() and g_object_unref() are considered to be invalid, and |
189 | | * have undefined behaviour. They will often fail with g_critical() or |
190 | | * g_warning(), but this must not be relied on. |
191 | | * |
192 | | * Callers should not assume that a class which implements #GAsyncInitable can |
193 | | * be initialized multiple times; for more information, see g_initable_init(). |
194 | | * If a class explicitly supports being initialized multiple times, |
195 | | * implementation requires yielding all subsequent calls to init_async() on the |
196 | | * results of the first call. |
197 | | * |
198 | | * For classes that also support the #GInitable interface, the default |
199 | | * implementation of this method will run the g_initable_init() function |
200 | | * in a thread, so if you want to support asynchronous initialization via |
201 | | * threads, just implement the #GAsyncInitable interface without overriding |
202 | | * any interface methods. |
203 | | * |
204 | | * Since: 2.22 |
205 | | */ |
206 | | void |
207 | | g_async_initable_init_async (GAsyncInitable *initable, |
208 | | int io_priority, |
209 | | GCancellable *cancellable, |
210 | | GAsyncReadyCallback callback, |
211 | | gpointer user_data) |
212 | 0 | { |
213 | 0 | GAsyncInitableIface *iface; |
214 | |
|
215 | 0 | g_return_if_fail (G_IS_ASYNC_INITABLE (initable)); |
216 | | |
217 | 0 | iface = G_ASYNC_INITABLE_GET_IFACE (initable); |
218 | |
|
219 | 0 | (* iface->init_async) (initable, io_priority, cancellable, callback, user_data); |
220 | 0 | } |
221 | | |
222 | | /** |
223 | | * g_async_initable_init_finish: |
224 | | * @initable: a #GAsyncInitable. |
225 | | * @res: a #GAsyncResult. |
226 | | * @error: a #GError location to store the error occurring, or %NULL to |
227 | | * ignore. |
228 | | * |
229 | | * Finishes asynchronous initialization and returns the result. |
230 | | * See g_async_initable_init_async(). |
231 | | * |
232 | | * Returns: %TRUE if successful. If an error has occurred, this function |
233 | | * will return %FALSE and set @error appropriately if present. |
234 | | * |
235 | | * Since: 2.22 |
236 | | */ |
237 | | gboolean |
238 | | g_async_initable_init_finish (GAsyncInitable *initable, |
239 | | GAsyncResult *res, |
240 | | GError **error) |
241 | 0 | { |
242 | 0 | GAsyncInitableIface *iface; |
243 | |
|
244 | 0 | g_return_val_if_fail (G_IS_ASYNC_INITABLE (initable), FALSE); |
245 | 0 | g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); |
246 | | |
247 | 0 | if (g_async_result_legacy_propagate_error (res, error)) |
248 | 0 | return FALSE; |
249 | | |
250 | 0 | iface = G_ASYNC_INITABLE_GET_IFACE (initable); |
251 | |
|
252 | 0 | return (* iface->init_finish) (initable, res, error); |
253 | 0 | } |
254 | | |
255 | | static void |
256 | | async_init_thread (GTask *task, |
257 | | gpointer source_object, |
258 | | gpointer task_data, |
259 | | GCancellable *cancellable) |
260 | 0 | { |
261 | 0 | GError *error = NULL; |
262 | |
|
263 | 0 | if (g_initable_init (G_INITABLE (source_object), cancellable, &error)) |
264 | 0 | g_task_return_boolean (task, TRUE); |
265 | 0 | else |
266 | 0 | g_task_return_error (task, error); |
267 | 0 | } |
268 | | |
269 | | static void |
270 | | g_async_initable_real_init_async (GAsyncInitable *initable, |
271 | | int io_priority, |
272 | | GCancellable *cancellable, |
273 | | GAsyncReadyCallback callback, |
274 | | gpointer user_data) |
275 | 0 | { |
276 | 0 | GTask *task; |
277 | |
|
278 | 0 | g_return_if_fail (G_IS_INITABLE (initable)); |
279 | | |
280 | 0 | task = g_task_new (initable, cancellable, callback, user_data); |
281 | 0 | g_task_set_source_tag (task, g_async_initable_real_init_async); |
282 | 0 | g_task_set_priority (task, io_priority); |
283 | 0 | g_task_run_in_thread (task, async_init_thread); |
284 | 0 | g_object_unref (task); |
285 | 0 | } |
286 | | |
287 | | static gboolean |
288 | | g_async_initable_real_init_finish (GAsyncInitable *initable, |
289 | | GAsyncResult *res, |
290 | | GError **error) |
291 | 0 | { |
292 | | /* For backward compatibility we have to process GSimpleAsyncResults |
293 | | * even though g_async_initable_real_init_async doesn't generate |
294 | | * them any more. |
295 | | */ |
296 | 0 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
297 | 0 | if (G_IS_SIMPLE_ASYNC_RESULT (res)) |
298 | 0 | { |
299 | 0 | GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); |
300 | 0 | if (g_simple_async_result_propagate_error (simple, error)) |
301 | 0 | return FALSE; |
302 | 0 | else |
303 | 0 | return TRUE; |
304 | 0 | } |
305 | 0 | G_GNUC_END_IGNORE_DEPRECATIONS |
306 | | |
307 | 0 | g_return_val_if_fail (g_task_is_valid (res, initable), FALSE); |
308 | | |
309 | 0 | return g_task_propagate_boolean (G_TASK (res), error); |
310 | 0 | } |
311 | | |
312 | | /** |
313 | | * g_async_initable_new_async: |
314 | | * @object_type: a #GType supporting #GAsyncInitable. |
315 | | * @io_priority: the [I/O priority][io-priority] of the operation |
316 | | * @cancellable: optional #GCancellable object, %NULL to ignore. |
317 | | * @callback: a #GAsyncReadyCallback to call when the initialization is |
318 | | * finished |
319 | | * @user_data: the data to pass to callback function |
320 | | * @first_property_name: (nullable): the name of the first property, or %NULL if no |
321 | | * properties |
322 | | * @...: the value of the first property, followed by other property |
323 | | * value pairs, and ended by %NULL. |
324 | | * |
325 | | * Helper function for constructing #GAsyncInitable object. This is |
326 | | * similar to g_object_new() but also initializes the object asynchronously. |
327 | | * |
328 | | * When the initialization is finished, @callback will be called. You can |
329 | | * then call g_async_initable_new_finish() to get the new object and check |
330 | | * for any errors. |
331 | | * |
332 | | * Since: 2.22 |
333 | | */ |
334 | | void |
335 | | g_async_initable_new_async (GType object_type, |
336 | | int io_priority, |
337 | | GCancellable *cancellable, |
338 | | GAsyncReadyCallback callback, |
339 | | gpointer user_data, |
340 | | const gchar *first_property_name, |
341 | | ...) |
342 | 0 | { |
343 | 0 | va_list var_args; |
344 | |
|
345 | 0 | va_start (var_args, first_property_name); |
346 | 0 | g_async_initable_new_valist_async (object_type, |
347 | 0 | first_property_name, var_args, |
348 | 0 | io_priority, cancellable, |
349 | 0 | callback, user_data); |
350 | 0 | va_end (var_args); |
351 | 0 | } |
352 | | |
353 | | /** |
354 | | * g_async_initable_newv_async: |
355 | | * @object_type: a #GType supporting #GAsyncInitable. |
356 | | * @n_parameters: the number of parameters in @parameters |
357 | | * @parameters: the parameters to use to construct the object |
358 | | * @io_priority: the [I/O priority][io-priority] of the operation |
359 | | * @cancellable: optional #GCancellable object, %NULL to ignore. |
360 | | * @callback: a #GAsyncReadyCallback to call when the initialization is |
361 | | * finished |
362 | | * @user_data: the data to pass to callback function |
363 | | * |
364 | | * Helper function for constructing #GAsyncInitable object. This is |
365 | | * similar to g_object_newv() but also initializes the object asynchronously. |
366 | | * |
367 | | * When the initialization is finished, @callback will be called. You can |
368 | | * then call g_async_initable_new_finish() to get the new object and check |
369 | | * for any errors. |
370 | | * |
371 | | * Since: 2.22 |
372 | | * Deprecated: 2.54: Use g_object_new_with_properties() and |
373 | | * g_async_initable_init_async() instead. See #GParameter for more information. |
374 | | */ |
375 | | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
376 | | void |
377 | | g_async_initable_newv_async (GType object_type, |
378 | | guint n_parameters, |
379 | | GParameter *parameters, |
380 | | int io_priority, |
381 | | GCancellable *cancellable, |
382 | | GAsyncReadyCallback callback, |
383 | | gpointer user_data) |
384 | 0 | { |
385 | 0 | GObject *obj; |
386 | |
|
387 | 0 | g_return_if_fail (G_TYPE_IS_ASYNC_INITABLE (object_type)); |
388 | | |
389 | 0 | obj = g_object_newv (object_type, n_parameters, parameters); |
390 | |
|
391 | 0 | g_async_initable_init_async (G_ASYNC_INITABLE (obj), |
392 | 0 | io_priority, cancellable, |
393 | 0 | callback, user_data); |
394 | 0 | g_object_unref (obj); /* Passed ownership to async call */ |
395 | 0 | } |
396 | | G_GNUC_END_IGNORE_DEPRECATIONS |
397 | | |
398 | | /** |
399 | | * g_async_initable_new_valist_async: |
400 | | * @object_type: a #GType supporting #GAsyncInitable. |
401 | | * @first_property_name: the name of the first property, followed by |
402 | | * the value, and other property value pairs, and ended by %NULL. |
403 | | * @var_args: The var args list generated from @first_property_name. |
404 | | * @io_priority: the [I/O priority][io-priority] of the operation |
405 | | * @cancellable: optional #GCancellable object, %NULL to ignore. |
406 | | * @callback: a #GAsyncReadyCallback to call when the initialization is |
407 | | * finished |
408 | | * @user_data: the data to pass to callback function |
409 | | * |
410 | | * Helper function for constructing #GAsyncInitable object. This is |
411 | | * similar to g_object_new_valist() but also initializes the object |
412 | | * asynchronously. |
413 | | * |
414 | | * When the initialization is finished, @callback will be called. You can |
415 | | * then call g_async_initable_new_finish() to get the new object and check |
416 | | * for any errors. |
417 | | * |
418 | | * Since: 2.22 |
419 | | */ |
420 | | void |
421 | | g_async_initable_new_valist_async (GType object_type, |
422 | | const gchar *first_property_name, |
423 | | va_list var_args, |
424 | | int io_priority, |
425 | | GCancellable *cancellable, |
426 | | GAsyncReadyCallback callback, |
427 | | gpointer user_data) |
428 | 0 | { |
429 | 0 | GObject *obj; |
430 | |
|
431 | 0 | g_return_if_fail (G_TYPE_IS_ASYNC_INITABLE (object_type)); |
432 | | |
433 | 0 | obj = g_object_new_valist (object_type, |
434 | 0 | first_property_name, |
435 | 0 | var_args); |
436 | |
|
437 | 0 | g_async_initable_init_async (G_ASYNC_INITABLE (obj), |
438 | 0 | io_priority, cancellable, |
439 | 0 | callback, user_data); |
440 | 0 | g_object_unref (obj); /* Passed ownership to async call */ |
441 | 0 | } |
442 | | |
443 | | /** |
444 | | * g_async_initable_new_finish: |
445 | | * @initable: the #GAsyncInitable from the callback |
446 | | * @res: the #GAsyncResult from the callback |
447 | | * @error: return location for errors, or %NULL to ignore |
448 | | * |
449 | | * Finishes the async construction for the various g_async_initable_new |
450 | | * calls, returning the created object or %NULL on error. |
451 | | * |
452 | | * Returns: (type GObject.Object) (transfer full): a newly created #GObject, |
453 | | * or %NULL on error. Free with g_object_unref(). |
454 | | * |
455 | | * Since: 2.22 |
456 | | */ |
457 | | GObject * |
458 | | g_async_initable_new_finish (GAsyncInitable *initable, |
459 | | GAsyncResult *res, |
460 | | GError **error) |
461 | 0 | { |
462 | 0 | if (g_async_initable_init_finish (initable, res, error)) |
463 | 0 | return g_object_ref (G_OBJECT (initable)); |
464 | 0 | else |
465 | 0 | return NULL; |
466 | 0 | } |