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