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