GCC Code Coverage Report


Directory: ./
File: shell/cc-object-storage.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 42 129 32.6%
Functions: 11 17 64.7%
Branches: 22 115 19.1%

Line Branch Exec Source
1 /* cc-object-storage.h
2 *
3 * Copyright 2018 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #define G_LOG_DOMAIN "cc-object-storage"
20
21 #include "cc-object-storage.h"
22
23 struct _CcObjectStorage
24 {
25 GObject parent_instance;
26
27 GHashTable *id_to_object;
28 };
29
30
6/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 43 times.
92 G_DEFINE_TYPE (CcObjectStorage, cc_object_storage, G_TYPE_OBJECT)
31
32 /* Singleton instance */
33 static CcObjectStorage *_instance = NULL;
34
35 /* GTask API to create a new D-Bus proxy */
36 typedef struct
37 {
38 GBusType bus_type;
39 GDBusProxyFlags flags;
40 gchar *name;
41 gchar *path;
42 gchar *interface;
43 gboolean cached;
44 } TaskData;
45
46 static TaskData*
47 task_data_new (GBusType bus_type,
48 GDBusProxyFlags flags,
49 const gchar *name,
50 const gchar *path,
51 const gchar *interface)
52 {
53 TaskData *data = g_slice_new (TaskData);
54 data->bus_type = bus_type;
55 data->flags =flags;
56 data->name = g_strdup (name);
57 data->path = g_strdup (path);
58 data->interface = g_strdup (interface);
59 data->cached = FALSE;
60
61 return data;
62 }
63
64 static void
65 task_data_free (TaskData *data)
66 {
67 g_free (data->name);
68 g_free (data->path);
69 g_free (data->interface);
70 g_slice_free (TaskData, data);
71 }
72
73 static void
74 create_dbus_proxy_in_thread_cb (GTask *task,
75 gpointer source_object,
76 gpointer task_data,
77 GCancellable *cancellable)
78 {
79 g_autoptr(GDBusProxy) proxy = NULL;
80 g_autoptr(GError) local_error = NULL;
81 TaskData *data = task_data;
82
83 proxy = g_dbus_proxy_new_for_bus_sync (data->bus_type,
84 data->flags,
85 NULL,
86 data->name,
87 data->path,
88 data->interface,
89 cancellable,
90 &local_error);
91
92 if (local_error)
93 {
94 g_task_return_error (task, g_steal_pointer (&local_error));
95 return;
96 }
97
98 g_task_return_pointer (task, g_object_ref (g_steal_pointer (&proxy)), g_object_unref);
99 }
100
101 static void
102 11 cc_object_storage_finalize (GObject *object)
103 {
104 11 CcObjectStorage *self = (CcObjectStorage *)object;
105
106 11 g_debug ("Destroying cached objects");
107
108
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 g_clear_pointer (&self->id_to_object, g_hash_table_destroy);
109
110 11 G_OBJECT_CLASS (cc_object_storage_parent_class)->finalize (object);
111 11 }
112
113 static void
114 1 cc_object_storage_class_init (CcObjectStorageClass *klass)
115 {
116 1 GObjectClass *object_class = G_OBJECT_CLASS (klass);
117
118 1 object_class->finalize = cc_object_storage_finalize;
119 1 }
120
121 static void
122 11 cc_object_storage_init (CcObjectStorage *self)
123 {
124 11 self->id_to_object = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
125 11 }
126
127 /**
128 * cc_object_storage_has_object:
129 * @key: the unique string identifier of the object
130 *
131 * Checks whether there is an object associated with @key.
132 *
133 * Returns: %TRUE if the object is stored, %FALSE otherwise.
134 */
135 gboolean
136 11 cc_object_storage_has_object (const gchar *key)
137 {
138
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 g_assert (CC_IS_OBJECT_STORAGE (_instance));
139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 g_assert (key != NULL);
140
141 11 return g_hash_table_contains (_instance->id_to_object, key);
142 }
143
144 /**
145 * cc_object_storage_add_object:
146 * @key: the unique string identifier of the object
147 * @object: (type GObject): the object to be stored
148 *
149 * Adds @object to the object storage. It is a programming error to try to
150 * add an object that was already added.
151 *
152 * @object must be a GObject.
153 *
154 * Always check if the object is stored with cc_object_storage_has_object()
155 * before calling this function.
156 */
157 void
158 11 cc_object_storage_add_object (const gchar *key,
159 gpointer object)
160 {
161 /* Trying to add an object that was already added is a hard error. Each
162 * object must be added once, and only once, over the entire lifetime
163 * of the application.
164 */
165
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 g_assert (CC_IS_OBJECT_STORAGE (_instance));
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 g_assert (key != NULL);
167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 g_assert (G_IS_OBJECT (object));
168
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 g_assert (!g_hash_table_contains (_instance->id_to_object, key));
169
170 11 g_debug ("Adding object %s (%s → %p) to the storage",
171 g_type_name (G_OBJECT_TYPE (object)),
172 key,
173 object);
174
175 22 g_hash_table_insert (_instance->id_to_object, g_strdup (key), g_object_ref (object));
176 11 }
177
178 /**
179 * cc_object_storage_get_object:
180 * @key: the unique string identifier of the object
181 *
182 * Retrieves the object associated with @key. It is a programming error to
183 * try to retrieve an object before adding it.
184 *
185 * Always check if the object is stored with cc_object_storage_has_object()
186 * before calling this function.
187 *
188 * Returns: (transfer full): the GObject associated with @key.
189 */
190 gpointer
191 11 cc_object_storage_get_object (const gchar *key)
192 {
193 /* Trying to peek an object that was not yet added is a hard error. Users
194 * of this API need to first check if the object is available with
195 * cc_object_storage_has_object().
196 */
197
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 g_assert (CC_IS_OBJECT_STORAGE (_instance));
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 g_assert (key != NULL);
199
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 g_assert (g_hash_table_contains (_instance->id_to_object, key));
200
201 11 return g_object_ref (g_hash_table_lookup (_instance->id_to_object, key));
202 }
203
204 /**
205 * cc_object_storage_create_dbus_proxy_sync:
206 * @name: the D-Bus name
207 * @flags: the D-Bus proxy flags
208 * @path: the D-Bus object path
209 * @interface: the D-Bus interface name
210 * @cancellable: (nullable): #GCancellable to cancel the operation
211 * @error: (nullable): return location for a #GError
212 *
213 * Synchronously create a #GDBusProxy with @name, @path and @interface,
214 * stores it in the cache, and returns the newly created proxy.
215 *
216 * If a proxy with that signature is already created, it will be used
217 * instead of creating a new one.
218 *
219 * Returns: (transfer full)(nullable): the new #GDBusProxy.
220 */
221 gpointer
222 cc_object_storage_create_dbus_proxy_sync (GBusType bus_type,
223 GDBusProxyFlags flags,
224 const gchar *name,
225 const gchar *path,
226 const gchar *interface,
227 GCancellable *cancellable,
228 GError **error)
229 {
230 g_autoptr(GDBusProxy) proxy = NULL;
231 g_autoptr(GError) local_error = NULL;
232 g_autofree gchar *key = NULL;
233
234 g_assert (CC_IS_OBJECT_STORAGE (_instance));
235 g_assert (name && *name);
236 g_assert (path && *path);
237 g_assert (interface && *interface);
238 g_assert (!error || !*error);
239
240 key = g_strdup_printf ("CcObjectStorage::dbus-proxy(%s,%s,%s)", name, path, interface);
241
242 g_debug ("Creating D-Bus proxy for %s", key);
243
244 /* Check if a DBus proxy with that signature is already available; if it is,
245 * return that instead of a new one.
246 */
247 if (g_hash_table_contains (_instance->id_to_object, key))
248 return cc_object_storage_get_object (key);
249
250 proxy = g_dbus_proxy_new_for_bus_sync (bus_type,
251 flags,
252 NULL,
253 name,
254 path,
255 interface,
256 cancellable,
257 &local_error);
258
259 if (local_error)
260 {
261 g_propagate_error (error, g_steal_pointer (&local_error));
262 return NULL;
263 }
264
265 /* Store the newly created D-Bus proxy */
266 cc_object_storage_add_object (key, proxy);
267
268 return g_steal_pointer (&proxy);
269 }
270
271
272 /**
273 * cc_object_storage_create_dbus_proxy:
274 * @name: the D-Bus name
275 * @flags: the D-Bus proxy flags
276 * @path: the D-Bus object path
277 * @interface: the D-Bus interface name
278 * @cancellable: (nullable): #GCancellable to cancel the operation
279 * @callback: callback for when the async operation is finished
280 * @user_data: user data for @callback
281 *
282 * Asynchronously create a #GDBusProxy with @name, @path and @interface.
283 *
284 * If a proxy with that signature is already created, it will be used instead of
285 * creating a new one.
286 *
287 * It is a programming error to create the an identical proxy while asynchronously
288 * creating one. Not cancelling this operation will result in an assertion failure
289 * when calling cc_object_storage_create_dbus_proxy_finish().
290 */
291 void
292 cc_object_storage_create_dbus_proxy (GBusType bus_type,
293 GDBusProxyFlags flags,
294 const gchar *name,
295 const gchar *path,
296 const gchar *interface,
297 GCancellable *cancellable,
298 GAsyncReadyCallback callback,
299 gpointer user_data)
300 {
301 g_autoptr(GTask) task = NULL;
302 g_autofree gchar *key = NULL;
303 TaskData *data = NULL;
304
305 g_assert (CC_IS_OBJECT_STORAGE (_instance));
306 g_assert (name && *name);
307 g_assert (path && *path);
308 g_assert (interface && *interface);
309 g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
310
311 data = task_data_new (bus_type, flags, name, path, interface);
312
313 task = g_task_new (_instance, cancellable, callback, user_data);
314 g_task_set_source_tag (task, cc_object_storage_create_dbus_proxy);
315 g_task_set_task_data (task, data, (GDestroyNotify) task_data_free);
316
317 /* Check if the D-Bus proxy is already created */
318 key = g_strdup_printf ("CcObjectStorage::dbus-proxy(%s,%s,%s)", name, path, interface);
319
320 g_debug ("Asynchronously creating D-Bus proxy for %s", key);
321
322 if (g_hash_table_contains (_instance->id_to_object, key))
323 {
324 /* Mark this GTask as already cached, so we can call the right assertions
325 * on the callback
326 * */
327 data->cached = TRUE;
328
329 g_debug ("Found in cache the D-Bus proxy %s", key);
330
331 g_task_return_pointer (task, cc_object_storage_get_object (key), g_object_unref);
332 return;
333 }
334
335 g_task_run_in_thread (task, create_dbus_proxy_in_thread_cb);
336 }
337
338 /**
339 * cc_object_storage_create_dbus_proxy_finish:
340 * @result:
341 * @error: (nullable): return location for a #GError
342 *
343 * Finishes a D-Bus proxy creation started by cc_object_storage_create_dbus_proxy().
344 *
345 * Synchronously create a #GDBusProxy with @name, @path and @interface,
346 * stores it in the cache, and returns the newly created proxy.
347 *
348 * If a proxy with that signature is already created, it will be used
349 * instead of creating a new one.
350 *
351 * Returns: (transfer full)(nullable): the new #GDBusProxy.
352 */
353 gpointer
354 cc_object_storage_create_dbus_proxy_finish (GAsyncResult *result,
355 GError **error)
356 {
357 g_autoptr(GDBusProxy) proxy = NULL;
358 g_autoptr(GError) local_error = NULL;
359 g_autofree gchar *key = NULL;
360 TaskData *task_data;
361 GTask *task;
362
363 task = G_TASK (result);
364
365 g_assert (task && G_TASK (result));
366 g_assert (!error || !*error);
367
368 task_data = g_task_get_task_data (task);
369 g_assert (task_data != NULL);
370
371 key = g_strdup_printf ("CcObjectStorage::dbus-proxy(%s,%s,%s)",
372 task_data->name,
373 task_data->path,
374 task_data->interface);
375
376 g_debug ("Finished creating D-Bus proxy for %s", key);
377
378 /* Retrieve the newly created proxy */
379 proxy = g_task_propagate_pointer (task, &local_error);
380
381 /* If the proxy is not cached, do the normal caching routine */
382 if (local_error)
383 {
384 g_propagate_error (error, g_steal_pointer (&local_error));
385 return NULL;
386 }
387
388 /* Either we have the object stored right when trying to create it - in which case,
389 * task_data->cached == TRUE and cc_object_storage_has_object (key) == TRUE - or we
390 * didn't have a cached proxy before, and we shouldn't have it now.
391 *
392 * This is to force consumers of this code to *never* try to create the same D-Bus
393 * proxy asynchronously multiple times. Trying to do so is considered a programming
394 * error.
395 */
396 g_assert (task_data->cached == cc_object_storage_has_object (key));
397
398 /* If the proxy is already cached, destroy the newly created and used the cached proxy
399 * instead.
400 */
401 if (cc_object_storage_has_object (key))
402 return cc_object_storage_get_object (key);
403
404 /* Store the newly created D-Bus proxy */
405 cc_object_storage_add_object (key, proxy);
406
407 return g_steal_pointer (&proxy);
408 }
409
410 /**
411 * cc_object_storage_initialize:
412 *
413 * Initializes the single CcObjectStorage. This must be called only once,
414 * and before every other method of this object.
415 */
416 void
417 11 cc_object_storage_initialize (void)
418 {
419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 g_assert (_instance == NULL);
420
421
3/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
11 if (g_once_init_enter (&_instance))
422 {
423 11 CcObjectStorage *instance = g_object_new (CC_TYPE_OBJECT_STORAGE, NULL);
424
425 11 g_debug ("Initializing object storage");
426
427 11 g_once_init_leave (&_instance, instance);
428 }
429 11 }
430
431 /**
432 * cc_object_storage_destroy:
433 *
434 * Destroys the instance of #CcObjectStorage. This must be called only
435 * once during the application lifetime. It is a programming error to
436 * call this function multiple times
437 */
438 void
439 11 cc_object_storage_destroy (void)
440 {
441
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 g_assert (_instance != NULL);
442
443
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 g_clear_object (&_instance);
444 11 }
445