Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: 2014-2019 Christian Hergert <chergert@redhat.com>
3 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
4 : :
5 : : #define G_LOG_DOMAIN "valent-object"
6 : :
7 : : #include "config.h"
8 : :
9 : : #include "valent-debug.h"
10 : : #include "valent-macros.h"
11 : : #include "valent-object.h"
12 : :
13 : : /**
14 : : * ValentObject:
15 : : *
16 : : * A base class for objects.
17 : : *
18 : : * `ValentObject` is a specialized `GObject` class, inspired by GNOME Builder's
19 : : * `IdeObject` and TinySPARQL's `TsparqlResource`. It provides a base class
20 : : * with helpers for working in threads and representing unique resources.
21 : : *
22 : : * Instances have a `GRecMutex` and a `GCancellable` that is created on-demand
23 : : * and triggered when the object is destroyed.
24 : : *
25 : : * Since: 1.0
26 : : */
27 : :
28 : : typedef struct
29 : : {
30 : : GRecMutex mutex;
31 : : GCancellable *cancellable;
32 : : char *iri;
33 : : ValentObject *parent;
34 : : GQueue children;
35 : : unsigned int in_destruction : 1;
36 : : unsigned int destroyed : 1;
37 : : } ValentObjectPrivate;
38 : :
39 [ + + + - ]: 47889 : G_DEFINE_TYPE_WITH_PRIVATE (ValentObject, valent_object, G_TYPE_OBJECT)
40 : :
41 : : typedef enum {
42 : : PROP_CANCELLABLE = 1,
43 : : PROP_IRI,
44 : : PROP_PARENT,
45 : : } ValentObjectProperty;
46 : :
47 : : static GParamSpec *properties[PROP_PARENT + 1] = { NULL, };
48 : :
49 : : enum {
50 : : DESTROY,
51 : : N_SIGNALS
52 : : };
53 : :
54 : : static guint signals[N_SIGNALS] = { 0, };
55 : :
56 : :
57 : : static GQueue finalizer_queue = G_QUEUE_INIT;
58 : : static GMutex finalizer_mutex;
59 : : static GSource *finalizer_source;
60 : :
61 : : static gboolean
62 : 535170 : valent_object_finalizer_source_check (GSource *source)
63 : : {
64 : 535170 : return finalizer_queue.length > 0;
65 : : }
66 : :
67 : : static gboolean
68 : 3 : valent_object_finalizer_source_dispatch (GSource *source,
69 : : GSourceFunc callback,
70 : : gpointer user_data)
71 : : {
72 : 3 : while (finalizer_queue.length)
73 : : {
74 [ + + ]: 6 : g_autoptr (GObject) object = g_queue_pop_head (&finalizer_queue);
75 [ - + ]: 3 : g_object_run_dispose (object);
76 : : }
77 : :
78 : 3 : return G_SOURCE_CONTINUE;
79 : : }
80 : :
81 : : static GSourceFuncs finalizer_source_funcs = {
82 : : .check = valent_object_finalizer_source_check,
83 : : .dispatch = valent_object_finalizer_source_dispatch,
84 : : };
85 : :
86 : :
87 : : static inline void
88 : 10560 : valent_object_private_lock (ValentObjectPrivate *priv)
89 : : {
90 : 10560 : g_rec_mutex_lock (&priv->mutex);
91 : 7278 : }
92 : :
93 : : static inline void
94 : 10560 : valent_object_private_unlock (ValentObjectPrivate *priv)
95 : : {
96 : 10560 : g_rec_mutex_unlock (&priv->mutex);
97 : 7517 : }
98 : :
99 : : /*
100 : : * ValentObject
101 : : */
102 : : static void
103 : 239 : valent_object_real_add (ValentObject *self,
104 : : ValentObject *child)
105 : : {
106 : 239 : ValentObjectPrivate *priv = valent_object_get_instance_private (self);
107 : 239 : ValentObjectPrivate *child_priv = valent_object_get_instance_private (child);
108 : :
109 [ - + ]: 239 : g_assert (VALENT_IS_OBJECT (self));
110 [ + - ]: 239 : g_assert (VALENT_IS_OBJECT (child));
111 : :
112 : 239 : valent_object_private_lock (priv);
113 : 239 : valent_object_private_lock (child_priv);
114 : :
115 [ - + ]: 239 : if (child_priv->parent != NULL)
116 : : {
117 : 0 : g_critical ("Attempt to add %s to %s, but it already has a parent",
118 : : G_OBJECT_TYPE_NAME (child),
119 : : G_OBJECT_TYPE_NAME (self));
120 : 0 : valent_object_private_unlock (child_priv);
121 : 0 : valent_object_private_unlock (priv);
122 : 0 : return;
123 : : }
124 : :
125 : 239 : g_queue_push_tail (&priv->children, g_object_ref (child));
126 : 239 : child_priv->parent = self;
127 : :
128 : 239 : valent_object_private_unlock (child_priv);
129 : 239 : valent_object_private_unlock (priv);
130 : : }
131 : :
132 : : static void
133 : 210 : valent_object_real_remove (ValentObject *self,
134 : : ValentObject *child)
135 : : {
136 : 210 : ValentObjectPrivate *priv = valent_object_get_instance_private (self);
137 : 210 : ValentObjectPrivate *child_priv = valent_object_get_instance_private (child);
138 : :
139 [ - + ]: 210 : g_assert (VALENT_IS_OBJECT (self));
140 [ + - ]: 210 : g_assert (VALENT_IS_OBJECT (child));
141 : :
142 : 210 : valent_object_private_lock (priv);
143 : 210 : valent_object_private_lock (child_priv);
144 : :
145 [ - + ]: 210 : if (child_priv->parent != self)
146 : : {
147 : 0 : g_critical ("Attempt to remove %s from incorrect parent %s",
148 : : G_OBJECT_TYPE_NAME (child),
149 : : G_OBJECT_TYPE_NAME (self));
150 : 0 : valent_object_private_unlock (child_priv);
151 : 0 : valent_object_private_unlock (priv);
152 : 0 : return;
153 : : }
154 : :
155 : 210 : g_queue_remove (&priv->children, child);
156 : 210 : child_priv->parent = NULL;
157 : :
158 : 210 : valent_object_private_unlock (child_priv);
159 : 210 : valent_object_private_unlock (priv);
160 : :
161 : 210 : g_object_unref (child);
162 : : }
163 : :
164 : : static void
165 : 1729 : valent_object_real_destroy (ValentObject *self)
166 : : {
167 : 1729 : ValentObjectPrivate *priv = valent_object_get_instance_private (self);
168 : 1729 : ValentObject *hold = NULL;
169 : :
170 [ - + ]: 1729 : g_assert (VALENT_IS_OBJECT (self));
171 : :
172 : 1729 : g_cancellable_cancel (priv->cancellable);
173 : :
174 [ + + ]: 1729 : if (priv->parent != NULL)
175 : : {
176 : 210 : hold = g_object_ref (self);
177 : 210 : valent_object_real_remove (priv->parent, self);
178 : : }
179 : :
180 [ + + ]: 1747 : while (priv->children.head != NULL)
181 : : {
182 : 18 : ValentObject *child = priv->children.head->data;
183 : 18 : valent_object_destroy (child);
184 : : }
185 : :
186 : 1729 : priv->destroyed = TRUE;
187 [ + + ]: 1729 : g_clear_object (&hold);
188 : 1729 : }
189 : :
190 : : /*
191 : : * GObject
192 : : */
193 : : static void
194 : 1730 : valent_object_dispose (GObject *object)
195 : : {
196 : 1730 : ValentObject *self = VALENT_OBJECT (object);
197 : 1730 : ValentObjectPrivate *priv = valent_object_get_instance_private (self);
198 : :
199 [ + + ]: 1730 : if (!VALENT_IS_MAIN_THREAD ())
200 : : {
201 : 1 : g_mutex_lock (&finalizer_mutex);
202 : 1 : g_queue_push_tail (&finalizer_queue, g_object_ref (self));
203 : 1 : g_mutex_unlock (&finalizer_mutex);
204 : 1 : g_main_context_wakeup (NULL);
205 : 1 : return;
206 : : }
207 : :
208 [ - + ]: 1729 : g_assert (VALENT_IS_OBJECT (object));
209 [ + - ]: 1729 : g_assert (VALENT_IS_MAIN_THREAD ());
210 : :
211 : 1729 : valent_object_private_lock (priv);
212 [ + - ]: 1729 : if (!priv->in_destruction)
213 : : {
214 : 1729 : priv->in_destruction = TRUE;
215 : 1729 : g_signal_emit (self, signals [DESTROY], 0);
216 : 1729 : priv->in_destruction = FALSE;
217 : : }
218 : 1729 : valent_object_private_unlock (priv);
219 : :
220 : 1729 : G_OBJECT_CLASS (valent_object_parent_class)->dispose (object);
221 : : }
222 : :
223 : : static void
224 : 1532 : valent_object_constructed (GObject *object)
225 : : {
226 [ - + ]: 1532 : if G_UNLIKELY (G_OBJECT_GET_CLASS (object)->dispose != valent_object_dispose)
227 : : {
228 : 0 : g_critical ("%s overrides dispose() instead of destroy()",
229 : : G_OBJECT_TYPE_NAME (object));
230 : : }
231 : :
232 : 1532 : G_OBJECT_CLASS (valent_object_parent_class)->constructed (object);
233 : 1532 : }
234 : :
235 : : static void
236 : 1396 : valent_object_finalize (GObject *object)
237 : : {
238 : 1396 : ValentObject *self = VALENT_OBJECT (object);
239 : 1396 : ValentObjectPrivate *priv = valent_object_get_instance_private (self);
240 : :
241 [ + + ]: 1396 : g_clear_object (&priv->cancellable);
242 [ + + ]: 1396 : g_clear_pointer (&priv->iri, g_free);
243 : 1396 : g_rec_mutex_clear (&priv->mutex);
244 : :
245 : 1396 : G_OBJECT_CLASS (valent_object_parent_class)->finalize (object);
246 : 1396 : }
247 : :
248 : : static void
249 : 45 : valent_object_get_property (GObject *object,
250 : : guint prop_id,
251 : : GValue *value,
252 : : GParamSpec *pspec)
253 : : {
254 : 45 : ValentObject *self = VALENT_OBJECT (object);
255 : :
256 [ + + + - ]: 45 : switch ((ValentObjectProperty)prop_id)
257 : : {
258 : 1 : case PROP_CANCELLABLE:
259 : 1 : g_value_take_object (value, valent_object_ref_cancellable (self));
260 : 1 : break;
261 : :
262 : 36 : case PROP_IRI:
263 : 36 : g_value_set_string (value, valent_object_get_iri (self));
264 : 36 : break;
265 : :
266 : 8 : case PROP_PARENT:
267 : 8 : g_value_set_object (value, valent_object_get_parent (self));
268 : 8 : break;
269 : :
270 : 0 : default:
271 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
272 : : }
273 : 45 : }
274 : :
275 : : static void
276 : 3858 : valent_object_set_property (GObject *object,
277 : : guint prop_id,
278 : : const GValue *value,
279 : : GParamSpec *pspec)
280 : : {
281 : 3858 : ValentObject *self = VALENT_OBJECT (object);
282 : 3858 : ValentObjectPrivate *priv = valent_object_get_instance_private (self);
283 : :
284 [ + + + - ]: 3858 : switch ((ValentObjectProperty)prop_id)
285 : : {
286 : 1532 : case PROP_CANCELLABLE:
287 : 1532 : priv->cancellable = g_value_dup_object (value);
288 : 1532 : break;
289 : :
290 : 1532 : case PROP_IRI:
291 : 1532 : priv->iri = g_value_dup_string (value);
292 : 1532 : break;
293 : :
294 : 794 : case PROP_PARENT:
295 : : {
296 : 794 : ValentObject *parent = g_value_get_object (value);
297 [ + + ]: 794 : if (parent != NULL)
298 : 239 : valent_object_real_add (parent, self);
299 : : }
300 : : break;
301 : :
302 : 0 : default:
303 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
304 : : }
305 : 3858 : }
306 : :
307 : : static void
308 : 60 : valent_object_class_init (ValentObjectClass *klass)
309 : : {
310 : 60 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
311 : :
312 : 60 : object_class->constructed = valent_object_constructed;
313 : 60 : object_class->dispose = valent_object_dispose;
314 : 60 : object_class->finalize = valent_object_finalize;
315 : 60 : object_class->get_property = valent_object_get_property;
316 : 60 : object_class->set_property = valent_object_set_property;
317 : :
318 : 60 : klass->destroy = valent_object_real_destroy;
319 : :
320 : : /**
321 : : * ValentObject:cancellable: (getter ref_cancellable)
322 : : *
323 : : * The object [class@Gio.Cancellable].
324 : : *
325 : : * A `GCancellable` that can be used by operations that should be cancelled
326 : : * when the object is destroyed (i.e. enters disposal).
327 : : *
328 : : * Since: 1.0
329 : : */
330 : 120 : properties [PROP_CANCELLABLE] =
331 : 60 : g_param_spec_object ("cancellable", NULL, NULL,
332 : : G_TYPE_CANCELLABLE,
333 : : (G_PARAM_READWRITE |
334 : : G_PARAM_CONSTRUCT_ONLY |
335 : : G_PARAM_EXPLICIT_NOTIFY |
336 : : G_PARAM_STATIC_STRINGS));
337 : :
338 : : /**
339 : : * ValentObject:iri: (getter get_iri)
340 : : *
341 : : * The object IRI.
342 : : *
343 : : * Since: 1.0
344 : : */
345 : 120 : properties [PROP_IRI] =
346 : 60 : g_param_spec_string ("iri", NULL, NULL,
347 : : NULL,
348 : : (G_PARAM_READWRITE |
349 : : G_PARAM_CONSTRUCT_ONLY |
350 : : G_PARAM_EXPLICIT_NOTIFY |
351 : : G_PARAM_STATIC_STRINGS));
352 : :
353 : : /**
354 : : * ValentObject:parent: (getter get_parent)
355 : : *
356 : : * The object's parent.
357 : : *
358 : : * Since: 1.0
359 : : */
360 : 120 : properties [PROP_PARENT] =
361 : 60 : g_param_spec_object ("parent", NULL, NULL,
362 : : VALENT_TYPE_OBJECT,
363 : : (G_PARAM_READWRITE |
364 : : G_PARAM_CONSTRUCT_ONLY |
365 : : G_PARAM_EXPLICIT_NOTIFY |
366 : : G_PARAM_STATIC_STRINGS));
367 : :
368 : 60 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
369 : :
370 : : /**
371 : : * ValentObject::destroy:
372 : : *
373 : : * Emitted when the object is being destroyed.
374 : : *
375 : : * This signal is emitted when the object enters disposal and always on the
376 : : * main thread, with the object lock acquired. Note that you must still drop
377 : : * any references you hold to avoid leaking memory.
378 : : *
379 : : * Implementations must override [vfunc@Valent.Object.destroy] instead of
380 : : * [vfunc@GObject.Object.dispose] to ensure the instance is finalized on the
381 : : * main thread.
382 : : *
383 : : * Since: 1.0
384 : : */
385 : 120 : signals [DESTROY] =
386 : 60 : g_signal_new ("destroy",
387 : : G_TYPE_FROM_CLASS (klass),
388 : : (G_SIGNAL_RUN_CLEANUP |
389 : : G_SIGNAL_NO_RECURSE |
390 : : G_SIGNAL_NO_HOOKS),
391 : : G_STRUCT_OFFSET (ValentObjectClass, destroy),
392 : : NULL, NULL,
393 : : g_cclosure_marshal_VOID__VOID,
394 : : G_TYPE_NONE, 0);
395 : 60 : g_signal_set_va_marshaller (signals [DESTROY],
396 : : G_TYPE_FROM_CLASS (klass),
397 : : g_cclosure_marshal_VOID__VOIDv);
398 : :
399 : : /* Setup the finalizer in main thread to receive off-thread objects */
400 : 60 : finalizer_source = g_source_new (&finalizer_source_funcs, sizeof (GSource));
401 : 60 : g_source_set_static_name (finalizer_source, "[valent-object-finalizer]");
402 : 60 : g_source_set_priority (finalizer_source, G_MAXINT);
403 : 60 : g_source_attach (finalizer_source, NULL);
404 : 60 : }
405 : :
406 : : static void
407 : 1532 : valent_object_init (ValentObject *self)
408 : : {
409 : 1532 : ValentObjectPrivate *priv = valent_object_get_instance_private (self);
410 : :
411 : 1532 : g_rec_mutex_init (&priv->mutex);
412 : 1532 : }
413 : :
414 : : /**
415 : : * valent_object_lock:
416 : : * @object: a `ValentObject`
417 : : *
418 : : * Acquire a lock on @object.
419 : : *
420 : : * Call [method@Valent.Object.unlock] to release the lock.
421 : : *
422 : : * Since: 1.0
423 : : */
424 : : void
425 : 7278 : valent_object_lock (ValentObject *object)
426 : : {
427 : 7278 : ValentObjectPrivate *priv = valent_object_get_instance_private (object);
428 : :
429 [ - + ]: 7278 : g_return_if_fail (VALENT_IS_OBJECT (object));
430 : :
431 : 7278 : valent_object_private_lock (priv);
432 : : }
433 : :
434 : : /**
435 : : * valent_object_unlock:
436 : : * @object: a `ValentObject`
437 : : *
438 : : * Release a lock on @object.
439 : : *
440 : : * The lock must have previously been acquired by [method@Valent.Object.lock].
441 : : *
442 : : * Since: 1.0
443 : : */
444 : : void
445 : 7278 : valent_object_unlock (ValentObject *object)
446 : : {
447 : 7278 : ValentObjectPrivate *priv = valent_object_get_instance_private (object);
448 : :
449 [ - + ]: 7278 : g_return_if_fail (VALENT_IS_OBJECT (object));
450 : :
451 : 7278 : valent_object_private_unlock (priv);
452 : : }
453 : :
454 : : /**
455 : : * valent_object_ref_cancellable: (get-property cancellable)
456 : : * @object: a `ValentObject`
457 : : *
458 : : * Get a [class@Gio.Cancellable] for the object.
459 : : *
460 : : * Returns: (transfer full) (not nullable): @object's `GCancellable`
461 : : *
462 : : * Since: 1.0
463 : : */
464 : : GCancellable *
465 : 307 : valent_object_ref_cancellable (ValentObject *object)
466 : : {
467 : 307 : ValentObjectPrivate *priv = valent_object_get_instance_private (object);
468 : 307 : GCancellable *ret;
469 : :
470 [ - + ]: 307 : g_return_val_if_fail (VALENT_IS_OBJECT (object), NULL);
471 : :
472 : 307 : valent_object_private_lock (priv);
473 [ + + ]: 307 : if (priv->cancellable == NULL)
474 : 185 : priv->cancellable = g_cancellable_new ();
475 : 307 : ret = g_object_ref (priv->cancellable);
476 : 307 : valent_object_private_unlock (priv);
477 : :
478 : 307 : return g_steal_pointer (&ret);
479 : : }
480 : :
481 : : /**
482 : : * valent_object_get_iri: (get-property iri)
483 : : * @object: a `ValentResource`
484 : : *
485 : : * Gets the IRI of @object.
486 : : *
487 : : * This method may only be called from the main thread.
488 : : *
489 : : * Returns: (transfer none) (nullable): the IRI of @object
490 : : *
491 : : * Since: 1.0
492 : : */
493 : : const char *
494 : 87 : valent_object_get_iri (ValentObject *object)
495 : : {
496 : 87 : ValentObjectPrivate *priv = valent_object_get_instance_private (object);
497 : :
498 [ - + ]: 87 : g_return_val_if_fail (VALENT_IS_OBJECT (object), NULL);
499 : :
500 : 87 : return priv->iri;
501 : : }
502 : :
503 : : /**
504 : : * valent_object_get_parent: (get-property parent)
505 : : * @object: a `ValentObject`
506 : : *
507 : : * Get the parent [class@Valent.Object].
508 : : *
509 : : * This method may only be called from the main thread.
510 : : *
511 : : * Returns: (type Valent.Object) (transfer none) (nullable): @object's parent,
512 : : * or %NULL if unset
513 : : *
514 : : * Since: 1.0
515 : : */
516 : : gpointer
517 : 442 : valent_object_get_parent (ValentObject *object)
518 : : {
519 : 442 : ValentObjectPrivate *priv = valent_object_get_instance_private (object);
520 : :
521 [ - + ]: 442 : g_return_val_if_fail (VALENT_IS_OBJECT (object), NULL);
522 : :
523 : 442 : return priv->parent;
524 : : }
525 : :
526 : : /**
527 : : * valent_object_destroy:
528 : : * @object: a `ValentObject`
529 : : *
530 : : * Destroy the object.
531 : : *
532 : : * If called from the main thread, it calls [method@GObject.Object.run_dispose],
533 : : * which activates the object [class@Gio.Cancellable] and emits
534 : : * [signal@Valent.Object::destroy].
535 : : *
536 : : * If called from another thread, an idle source will be added to invoke it on
537 : : * the main thread.
538 : : *
539 : : * Since: 1.0
540 : : */
541 : : void
542 : 348 : valent_object_destroy (ValentObject *object)
543 : : {
544 : 348 : ValentObjectPrivate *priv = valent_object_get_instance_private (object);
545 : :
546 [ - + ]: 348 : g_return_if_fail (VALENT_IS_OBJECT (object));
547 : :
548 : 348 : g_object_ref (object);
549 : 348 : valent_object_private_lock (priv);
550 : :
551 [ + + ]: 348 : if (VALENT_IS_MAIN_THREAD ())
552 : : {
553 : 346 : g_cancellable_cancel (priv->cancellable);
554 : :
555 [ + - + + ]: 346 : if (!priv->in_destruction && !priv->destroyed)
556 : 330 : g_object_run_dispose (G_OBJECT (object));
557 : : }
558 : : else
559 : : {
560 : 2 : g_mutex_lock (&finalizer_mutex);
561 : 2 : g_queue_push_tail (&finalizer_queue, g_object_ref (object));
562 : 2 : g_mutex_unlock (&finalizer_mutex);
563 : : }
564 : :
565 : 348 : valent_object_private_unlock (priv);
566 : 348 : g_object_unref (object);
567 : : }
568 : :
569 : : /**
570 : : * valent_object_in_destruction:
571 : : * @object: a `ValentObject`
572 : : *
573 : : * Get whether the object is destroyed or in destruction.
574 : : *
575 : : * Returns: %TRUE if destroyed, or %FALSE if not
576 : : *
577 : : * Since: 1.0
578 : : */
579 : : gboolean
580 : 73 : valent_object_in_destruction (ValentObject *object)
581 : : {
582 : 73 : ValentObjectPrivate *priv = valent_object_get_instance_private (object);
583 : 73 : gboolean ret;
584 : :
585 [ - + ]: 73 : g_return_val_if_fail (VALENT_IS_OBJECT (object), FALSE);
586 : :
587 : 73 : valent_object_lock (object);
588 [ + + + + ]: 73 : ret = priv->in_destruction || priv->destroyed;
589 : 73 : valent_object_unlock (object);
590 : :
591 : 73 : return ret;
592 : : }
593 : :
|