Coverage Report

Created: 2026-07-12 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gstreamer/gst/gstminiobject.c
Line
Count
Source
1
/* GStreamer
2
 * Copyright (C) 2005 David Schleef <ds@schleef.org>
3
 *
4
 * gstminiobject.h: Header for GstMiniObject
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Library General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Library General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Library General Public
17
 * License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19
 * Boston, MA 02110-1301, USA.
20
 */
21
/**
22
 * SECTION:gstminiobject
23
 * @title: GstMiniObject
24
 * @short_description: Lightweight base class for the GStreamer object hierarchy
25
 *
26
 * #GstMiniObject is a simple structure that can be used to implement refcounted
27
 * types.
28
 *
29
 * Subclasses will include #GstMiniObject as the first member in their structure
30
 * and then call gst_mini_object_init() to initialize the #GstMiniObject fields.
31
 *
32
 * gst_mini_object_ref() and gst_mini_object_unref() increment and decrement the
33
 * refcount respectively. When the refcount of a mini-object reaches 0, the
34
 * dispose function is called first and when this returns %TRUE, the free
35
 * function of the miniobject is called.
36
 *
37
 * A copy can be made with gst_mini_object_copy().
38
 *
39
 * gst_mini_object_is_writable() will return %TRUE when the refcount of the
40
 * object is exactly 1 and there is no parent or a single parent exists and is
41
 * writable itself, meaning the current caller has the only reference to the
42
 * object. gst_mini_object_make_writable() will return a writable version of
43
 * the object, which might be a new copy when the refcount was not 1.
44
 *
45
 * Opaque data can be associated with a #GstMiniObject with
46
 * gst_mini_object_set_qdata() and gst_mini_object_get_qdata(). The data is
47
 * meant to be specific to the particular object and is not automatically copied
48
 * with gst_mini_object_copy() or similar methods.
49
 *
50
 * A weak reference can be added and remove with gst_mini_object_weak_ref()
51
 * and gst_mini_object_weak_unref() respectively.
52
 */
53
#ifdef HAVE_CONFIG_H
54
#include "config.h"
55
#endif
56
57
#include "gst/gst_private.h"
58
#include "gst/gstminiobject.h"
59
#include "gst/gstinfo.h"
60
#include <gobject/gvaluecollector.h>
61
62
GType _gst_mini_object_type = 0;
63
64
/* Mutex used for weak referencing */
65
G_LOCK_DEFINE_STATIC (qdata_mutex);
66
static GQuark weak_ref_quark;
67
68
1.26M
#define SHARE_ONE (1 << 16)
69
382k
#define SHARE_TWO (2 << 16)
70
#define SHARE_MASK (~(SHARE_ONE - 1))
71
487k
#define IS_SHARED(state) (state >= SHARE_TWO)
72
510k
#define LOCK_ONE (GST_LOCK_FLAG_LAST)
73
1.00M
#define FLAG_MASK (GST_LOCK_FLAG_LAST - 1)
74
#define LOCK_MASK ((SHARE_ONE - 1) - FLAG_MASK)
75
765k
#define LOCK_FLAG_MASK (SHARE_ONE - 1)
76
77
/**
78
 * GST_TYPE_MINI_OBJECT:
79
 *
80
 * The #GType associated with #GstMiniObject.
81
 *
82
 * Since: 1.20
83
 */
84
85
/* For backwards compatibility reasons we use the
86
 * guint and gpointer in the GstMiniObject struct in
87
 * a rather complicated way to store the parent(s) and qdata.
88
 * Originally the were just the number of qdatas and the qdata.
89
 *
90
 * The guint is used as an atomic state integer with the following
91
 * states:
92
 * - Locked: 0, basically a spinlock
93
 * - No parent, no qdata: 1 (pointer is NULL)
94
 * - One parent: 2 (pointer contains the parent)
95
 * - Multiple parents or qdata: 3 (pointer contains a PrivData struct)
96
 *
97
 * Unless we're in state 3, we always have to move to Locking state
98
 * atomically and release that again later to the target state whenever
99
 * accessing the pointer. When we're in state 3, we will never move to lower
100
 * states again
101
 *
102
 * FIXME 2.0: We should store this directly inside the struct, possibly
103
 * keeping space directly allocated for a couple of parents
104
 */
105
106
enum
107
{
108
  PRIV_DATA_STATE_LOCKED = 0,
109
  PRIV_DATA_STATE_NO_PARENT = 1,
110
  PRIV_DATA_STATE_ONE_PARENT = 2,
111
  PRIV_DATA_STATE_PARENTS_OR_QDATA = 3,
112
};
113
114
typedef struct
115
{
116
  GQuark quark;
117
  GstMiniObjectNotify notify;
118
  gpointer data;
119
  GDestroyNotify destroy;
120
} GstQData;
121
122
typedef struct
123
{
124
  /* Atomic spinlock: 1 if locked, 0 otherwise */
125
  gint parent_lock;
126
  guint n_parents, n_parents_len;
127
  GstMiniObject **parents;
128
129
  guint n_qdata, n_qdata_len;
130
  GstQData *qdata;
131
} PrivData;
132
133
0
#define QDATA(q,i)          (q->qdata)[(i)]
134
0
#define QDATA_QUARK(o,i)    (QDATA(o,i).quark)
135
0
#define QDATA_NOTIFY(o,i)   (QDATA(o,i).notify)
136
0
#define QDATA_DATA(o,i)     (QDATA(o,i).data)
137
0
#define QDATA_DESTROY(o,i)  (QDATA(o,i).destroy)
138
139
5
GST_DEFINE_MINI_OBJECT_TYPE (GstMiniObject, gst_mini_object);
140
5
141
5
void
142
5
_priv_gst_mini_object_initialize (void)
143
5
{
144
5
  _gst_mini_object_type = gst_mini_object_get_type ();
145
5
  weak_ref_quark = g_quark_from_static_string ("GstMiniObjectWeakRefQuark");
146
5
}
147
148
/**
149
 * gst_mini_object_init: (skip)
150
 * @mini_object: a #GstMiniObject
151
 * @flags: initial #GstMiniObjectFlags
152
 * @type: the #GType of the mini-object to create
153
 * @copy_func: (allow-none): the copy function, or %NULL
154
 * @dispose_func: (allow-none): the dispose function, or %NULL
155
 * @free_func: (allow-none): the free function or %NULL
156
 *
157
 * Initializes a mini-object with the desired type and copy/dispose/free
158
 * functions.
159
 */
160
void
161
gst_mini_object_init (GstMiniObject * mini_object, guint flags, GType type,
162
    GstMiniObjectCopyFunction copy_func,
163
    GstMiniObjectDisposeFunction dispose_func,
164
    GstMiniObjectFreeFunction free_func)
165
1.65M
{
166
1.65M
  mini_object->type = type;
167
1.65M
  mini_object->refcount = 1;
168
1.65M
  mini_object->lockstate = 0;
169
1.65M
  mini_object->flags = flags;
170
171
1.65M
  mini_object->copy = copy_func;
172
1.65M
  mini_object->dispose = dispose_func;
173
1.65M
  mini_object->free = free_func;
174
175
1.65M
  g_atomic_int_set ((gint *) & mini_object->priv_uint,
176
1.65M
      PRIV_DATA_STATE_NO_PARENT);
177
1.65M
  mini_object->priv_pointer = NULL;
178
179
1.65M
  GST_TRACER_MINI_OBJECT_CREATED (mini_object);
180
1.65M
}
181
182
/**
183
 * gst_mini_object_copy: (skip)
184
 * @mini_object: the mini-object to copy
185
 *
186
 * Creates a copy of the mini-object.
187
 *
188
 * MT safe
189
 *
190
 * Returns: (transfer full) (nullable): the new mini-object if copying is
191
 * possible, %NULL otherwise.
192
 */
193
GstMiniObject *
194
gst_mini_object_copy (const GstMiniObject * mini_object)
195
12.7k
{
196
12.7k
  GstMiniObject *copy;
197
198
12.7k
  g_return_val_if_fail (mini_object != NULL, NULL);
199
200
12.7k
  if (mini_object->copy)
201
12.7k
    copy = mini_object->copy (mini_object);
202
0
  else
203
0
    copy = NULL;
204
205
12.7k
  return copy;
206
12.7k
}
207
208
/**
209
 * gst_mini_object_lock:
210
 * @object: the mini-object to lock
211
 * @flags: #GstLockFlags
212
 *
213
 * Lock the mini-object with the specified access mode in @flags.
214
 *
215
 * Returns: %TRUE if @object could be locked.
216
 */
217
gboolean
218
gst_mini_object_lock (GstMiniObject * object, GstLockFlags flags)
219
504k
{
220
504k
  guint access_mode, state, newstate;
221
222
504k
  g_return_val_if_fail (object != NULL, FALSE);
223
504k
  g_return_val_if_fail (GST_MINI_OBJECT_IS_LOCKABLE (object), FALSE);
224
225
504k
  if (G_UNLIKELY (object->flags & GST_MINI_OBJECT_FLAG_LOCK_READONLY &&
226
504k
          flags & GST_LOCK_FLAG_WRITE))
227
0
    return FALSE;
228
229
504k
  do {
230
504k
    access_mode = flags & FLAG_MASK;
231
504k
    newstate = state = (guint) g_atomic_int_get (&object->lockstate);
232
233
504k
    GST_CAT_TRACE (GST_CAT_LOCKING, "lock %p: state %08x, access_mode %u",
234
504k
        object, state, access_mode);
235
236
504k
    if (access_mode & GST_LOCK_FLAG_EXCLUSIVE) {
237
      /* shared ref */
238
249k
      newstate += SHARE_ONE;
239
249k
      access_mode &= ~GST_LOCK_FLAG_EXCLUSIVE;
240
249k
    }
241
242
    /* shared counter > 1 and write access is not allowed */
243
504k
    if (((state & GST_LOCK_FLAG_WRITE) != 0
244
504k
            || (access_mode & GST_LOCK_FLAG_WRITE) != 0)
245
139k
        && IS_SHARED (newstate))
246
0
      goto lock_failed;
247
248
504k
    if (access_mode) {
249
255k
      if ((state & LOCK_FLAG_MASK) == 0) {
250
        /* nothing mapped, set access_mode */
251
255k
        newstate |= access_mode;
252
255k
      } else {
253
        /* access_mode must match */
254
146
        if ((state & access_mode) != access_mode)
255
0
          goto lock_failed;
256
146
      }
257
      /* increase refcount */
258
255k
      newstate += LOCK_ONE;
259
255k
    }
260
504k
  } while (!g_atomic_int_compare_and_exchange (&object->lockstate, state,
261
504k
          newstate));
262
263
504k
  return TRUE;
264
265
0
lock_failed:
266
0
  {
267
0
    GST_CAT_DEBUG (GST_CAT_LOCKING,
268
0
        "lock failed %p: state %08x, access_mode %u", object, state,
269
0
        access_mode);
270
0
    return FALSE;
271
504k
  }
272
504k
}
273
274
/**
275
 * gst_mini_object_unlock:
276
 * @object: the mini-object to unlock
277
 * @flags: #GstLockFlags
278
 *
279
 * Unlock the mini-object with the specified access mode in @flags.
280
 */
281
void
282
gst_mini_object_unlock (GstMiniObject * object, GstLockFlags flags)
283
504k
{
284
504k
  guint access_mode, state, newstate;
285
286
504k
  g_return_if_fail (object != NULL);
287
504k
  g_return_if_fail (GST_MINI_OBJECT_IS_LOCKABLE (object));
288
289
504k
  do {
290
504k
    access_mode = flags & FLAG_MASK;
291
504k
    newstate = state = (guint) g_atomic_int_get (&object->lockstate);
292
293
504k
    GST_CAT_TRACE (GST_CAT_LOCKING, "unlock %p: state %08x, access_mode %u",
294
504k
        object, state, access_mode);
295
296
504k
    if (access_mode & GST_LOCK_FLAG_EXCLUSIVE) {
297
      /* shared counter */
298
249k
      g_return_if_fail (state >= SHARE_ONE);
299
249k
      newstate -= SHARE_ONE;
300
249k
      access_mode &= ~GST_LOCK_FLAG_EXCLUSIVE;
301
249k
    }
302
303
504k
    if (access_mode) {
304
255k
      g_return_if_fail ((state & access_mode) == access_mode);
305
      /* decrease the refcount */
306
255k
      newstate -= LOCK_ONE;
307
      /* last refcount, unset access_mode */
308
255k
      if ((newstate & LOCK_FLAG_MASK) == access_mode)
309
255k
        newstate &= ~LOCK_FLAG_MASK;
310
255k
    }
311
504k
  } while (!g_atomic_int_compare_and_exchange (&object->lockstate, state,
312
504k
          newstate));
313
504k
}
314
315
/* Locks the priv pointer and sets the priv uint to PRIV_DATA_STATE_LOCKED,
316
 * unless the full struct was already stored in the priv pointer.
317
 *
318
 * Returns the previous state of the priv uint
319
 */
320
static guint
321
lock_priv_pointer (GstMiniObject * object)
322
2.28M
{
323
2.28M
  gint priv_state = g_atomic_int_get ((gint *) & object->priv_uint);
324
325
2.28M
  if (priv_state != PRIV_DATA_STATE_PARENTS_OR_QDATA) {
326
    /* As long as the struct was not allocated yet and either someone else
327
     * locked it or our priv_state is out of date, try to lock it */
328
2.28M
    while (priv_state != PRIV_DATA_STATE_PARENTS_OR_QDATA &&
329
2.28M
        (priv_state == PRIV_DATA_STATE_LOCKED ||
330
2.28M
            !g_atomic_int_compare_and_exchange ((gint *) & object->priv_uint,
331
2.28M
                priv_state, PRIV_DATA_STATE_LOCKED)))
332
0
      priv_state = g_atomic_int_get ((gint *) & object->priv_uint);
333
334
    /* Note that if we got the full struct, we did not store
335
     * PRIV_DATA_STATE_LOCKED and did not actually lock the priv pointer */
336
2.28M
  }
337
338
2.28M
  return priv_state;
339
2.28M
}
340
341
/**
342
 * gst_mini_object_is_writable:
343
 * @mini_object: the mini-object to check
344
 *
345
 * If @mini_object has the LOCKABLE flag set, check if the current EXCLUSIVE
346
 * lock on @object is the only one, this means that changes to the object will
347
 * not be visible to any other object.
348
 *
349
 * If the LOCKABLE flag is not set, check if the refcount of @mini_object is
350
 * exactly 1, meaning that no other reference exists to the object and that the
351
 * object is therefore writable.
352
 *
353
 * Modification of a mini-object should only be done after verifying that it
354
 * is writable.
355
 *
356
 * Returns: %TRUE if the object is writable.
357
 */
358
gboolean
359
gst_mini_object_is_writable (const GstMiniObject * mini_object)
360
1.70M
{
361
1.70M
  gboolean result;
362
1.70M
  gint priv_state;
363
364
1.70M
  g_return_val_if_fail (mini_object != NULL, FALSE);
365
366
  /* Let's first check our own writability. If this already fails there's
367
   * no point in checking anything else */
368
1.70M
  if (GST_MINI_OBJECT_IS_LOCKABLE (mini_object)) {
369
243k
    result = !IS_SHARED (g_atomic_int_get (&mini_object->lockstate));
370
1.46M
  } else {
371
1.46M
    result = (GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) == 1);
372
1.46M
  }
373
1.70M
  if (!result)
374
7.53k
    return result;
375
376
  /* We are writable ourselves, but are there parents and are they all
377
   * writable too? */
378
1.69M
  priv_state = lock_priv_pointer (GST_MINI_OBJECT_CAST (mini_object));
379
380
  /* Now we either have to check the full struct and all the
381
   * parents in there, or if there is exactly one parent we
382
   * can check that one */
383
1.69M
  if (priv_state == PRIV_DATA_STATE_PARENTS_OR_QDATA) {
384
0
    PrivData *priv_data = mini_object->priv_pointer;
385
386
    /* Lock parents */
387
0
    while (!g_atomic_int_compare_and_exchange (&priv_data->parent_lock, 0, 1));
388
389
    /* If we have one parent, we're only writable if that parent is writable.
390
     * Otherwise if we have multiple parents we are not writable, and if
391
     * we have no parent, we are writable */
392
0
    if (priv_data->n_parents == 1)
393
0
      result = gst_mini_object_is_writable (priv_data->parents[0]);
394
0
    else if (priv_data->n_parents == 0)
395
0
      result = TRUE;
396
0
    else
397
0
      result = FALSE;
398
399
    /* Unlock again */
400
0
    g_atomic_int_set (&priv_data->parent_lock, 0);
401
1.69M
  } else {
402
1.69M
    if (priv_state == PRIV_DATA_STATE_ONE_PARENT) {
403
245k
      result = gst_mini_object_is_writable (mini_object->priv_pointer);
404
1.45M
    } else {
405
1.45M
      g_assert (priv_state == PRIV_DATA_STATE_NO_PARENT);
406
1.45M
      result = TRUE;
407
1.45M
    }
408
409
    /* Unlock again */
410
1.69M
    g_atomic_int_set ((gint *) & mini_object->priv_uint, priv_state);
411
1.69M
  }
412
413
1.69M
  return result;
414
1.69M
}
415
416
/**
417
 * gst_mini_object_make_writable: (skip)
418
 * @mini_object: (transfer full): the mini-object to make writable
419
 *
420
 * Checks if a mini-object is writable.  If not, a writable copy is made and
421
 * returned.  This gives away the reference to the original mini object,
422
 * and returns a reference to the new object.
423
 *
424
 * MT safe
425
 *
426
 * Returns: (transfer full) (nullable): a writable mini-object (which may or may not be
427
 *     the same as @mini_object) or %NULL if copying is required but not possible.
428
 */
429
GstMiniObject *
430
gst_mini_object_make_writable (GstMiniObject * mini_object)
431
69.8k
{
432
69.8k
  GstMiniObject *ret;
433
434
69.8k
  g_return_val_if_fail (mini_object != NULL, NULL);
435
436
69.8k
  if (gst_mini_object_is_writable (mini_object)) {
437
64.7k
    ret = mini_object;
438
64.7k
  } else {
439
5.11k
    ret = gst_mini_object_copy (mini_object);
440
5.11k
    GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy %s miniobject %p -> %p",
441
5.11k
        g_type_name (GST_MINI_OBJECT_TYPE (mini_object)), mini_object, ret);
442
5.11k
    gst_mini_object_unref (mini_object);
443
5.11k
  }
444
445
69.8k
  return ret;
446
69.8k
}
447
448
/**
449
 * gst_mini_object_ref: (skip)
450
 * @mini_object: the mini-object
451
 *
452
 * Increase the reference count of the mini-object.
453
 *
454
 * Note that the refcount affects the writability
455
 * of @mini-object, see gst_mini_object_is_writable(). It is
456
 * important to note that keeping additional references to
457
 * GstMiniObject instances can potentially increase the number
458
 * of memcpy operations in a pipeline, especially if the miniobject
459
 * is a #GstBuffer.
460
 *
461
 * Returns: (transfer full): the mini-object.
462
 */
463
GstMiniObject *
464
gst_mini_object_ref (GstMiniObject * mini_object)
465
2.51M
{
466
2.51M
  gint old_refcount, new_refcount;
467
468
2.51M
  g_return_val_if_fail (mini_object != NULL, NULL);
469
  /* we can't assert that the refcount > 0 since the _free functions
470
   * increments the refcount from 0 to 1 again to allow resurrecting
471
   * the object
472
   g_return_val_if_fail (mini_object->refcount > 0, NULL);
473
   */
474
475
2.51M
  old_refcount = g_atomic_int_add (&mini_object->refcount, 1);
476
2.51M
  new_refcount = old_refcount + 1;
477
478
2.51M
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p ref %d->%d", mini_object,
479
2.51M
      old_refcount, new_refcount);
480
481
2.51M
  GST_TRACER_MINI_OBJECT_REFFED (mini_object, new_refcount);
482
483
2.51M
  return mini_object;
484
2.51M
}
485
486
/* Called with global qdata lock */
487
static gint
488
find_notify (GstMiniObject * object, GQuark quark, gboolean match_notify,
489
    GstMiniObjectNotify notify, gpointer data)
490
0
{
491
0
  guint i;
492
0
  gint priv_state = g_atomic_int_get ((gint *) & object->priv_uint);
493
0
  PrivData *priv_data;
494
495
0
  if (priv_state != PRIV_DATA_STATE_PARENTS_OR_QDATA)
496
0
    return -1;
497
498
0
  priv_data = object->priv_pointer;
499
500
0
  for (i = 0; i < priv_data->n_qdata; i++) {
501
0
    if (QDATA_QUARK (priv_data, i) == quark) {
502
      /* check if we need to match the callback too */
503
0
      if (!match_notify || (QDATA_NOTIFY (priv_data, i) == notify &&
504
0
              QDATA_DATA (priv_data, i) == data))
505
0
        return i;
506
0
    }
507
0
  }
508
0
  return -1;
509
0
}
510
511
static void
512
remove_notify (GstMiniObject * object, gint index)
513
0
{
514
0
#ifndef G_DISABLE_ASSERT
515
0
  gint priv_state = g_atomic_int_get ((gint *) & object->priv_uint);
516
0
#endif
517
0
  PrivData *priv_data;
518
519
0
  g_assert (priv_state == PRIV_DATA_STATE_PARENTS_OR_QDATA);
520
0
  priv_data = object->priv_pointer;
521
522
  /* remove item */
523
0
  priv_data->n_qdata--;
524
0
  if (priv_data->n_qdata == 0) {
525
    /* we don't shrink but free when everything is gone */
526
0
    g_free (priv_data->qdata);
527
0
    priv_data->qdata = NULL;
528
0
    priv_data->n_qdata_len = 0;
529
0
  } else if (index != priv_data->n_qdata) {
530
0
    QDATA (priv_data, index) = QDATA (priv_data, priv_data->n_qdata);
531
0
  }
532
0
}
533
534
/* Make sure we allocate the PrivData of this object if not happened yet */
535
static void
536
ensure_priv_data (GstMiniObject * object)
537
9
{
538
9
  gint priv_state;
539
9
  PrivData *priv_data;
540
9
  GstMiniObject *parent = NULL;
541
542
9
  GST_CAT_DEBUG (GST_CAT_PERFORMANCE,
543
9
      "allocating private data %s miniobject %p",
544
9
      g_type_name (GST_MINI_OBJECT_TYPE (object)), object);
545
546
9
  priv_state = lock_priv_pointer (object);
547
9
  if (priv_state == PRIV_DATA_STATE_PARENTS_OR_QDATA)
548
0
    return;
549
550
  /* Now we're either locked, or someone has already allocated the struct
551
   * before us and we can just go ahead
552
   *
553
   * Note: if someone else allocated it in the meantime, we don't have to
554
   * unlock as we didn't lock! */
555
9
  if (priv_state != PRIV_DATA_STATE_PARENTS_OR_QDATA) {
556
9
    if (priv_state == PRIV_DATA_STATE_ONE_PARENT)
557
9
      parent = object->priv_pointer;
558
559
9
    object->priv_pointer = priv_data = g_new0 (PrivData, 1);
560
561
9
    if (parent) {
562
9
      priv_data->parents = g_new (GstMiniObject *, 16);
563
9
      priv_data->n_parents_len = 16;
564
9
      priv_data->n_parents = 1;
565
9
      priv_data->parents[0] = parent;
566
9
    }
567
568
    /* Unlock */
569
9
    g_atomic_int_set ((gint *) & object->priv_uint,
570
9
        PRIV_DATA_STATE_PARENTS_OR_QDATA);
571
9
  }
572
9
}
573
574
static void
575
set_notify (GstMiniObject * object, gint index, GQuark quark,
576
    GstMiniObjectNotify notify, gpointer data, GDestroyNotify destroy)
577
0
{
578
0
  PrivData *priv_data;
579
580
0
  ensure_priv_data (object);
581
0
  priv_data = object->priv_pointer;
582
583
0
  if (index == -1) {
584
    /* add item */
585
0
    index = priv_data->n_qdata++;
586
0
    if (index >= priv_data->n_qdata_len) {
587
0
      priv_data->n_qdata_len *= 2;
588
0
      if (priv_data->n_qdata_len == 0)
589
0
        priv_data->n_qdata_len = 16;
590
591
0
      priv_data->qdata =
592
0
          g_realloc (priv_data->qdata,
593
0
          sizeof (GstQData) * priv_data->n_qdata_len);
594
0
    }
595
0
  }
596
597
0
  QDATA_QUARK (priv_data, index) = quark;
598
0
  QDATA_NOTIFY (priv_data, index) = notify;
599
0
  QDATA_DATA (priv_data, index) = data;
600
0
  QDATA_DESTROY (priv_data, index) = destroy;
601
0
}
602
603
static void
604
free_priv_data (GstMiniObject * obj)
605
1.64M
{
606
1.64M
  guint i;
607
1.64M
  gint priv_state = g_atomic_int_get ((gint *) & obj->priv_uint);
608
1.64M
  PrivData *priv_data;
609
610
1.64M
  if (priv_state != PRIV_DATA_STATE_PARENTS_OR_QDATA) {
611
1.64M
    if (priv_state == PRIV_DATA_STATE_LOCKED) {
612
0
      g_warning
613
0
          ("%s: object finalizing but has locked private data (object:%p)",
614
0
          G_STRFUNC, obj);
615
1.64M
    } else if (priv_state == PRIV_DATA_STATE_ONE_PARENT) {
616
0
      g_warning
617
0
          ("%s: object finalizing but still has parent (object:%p, parent:%p)",
618
0
          G_STRFUNC, obj, obj->priv_pointer);
619
0
    }
620
621
1.64M
    return;
622
1.64M
  }
623
624
7
  priv_data = obj->priv_pointer;
625
626
7
  for (i = 0; i < priv_data->n_qdata; i++) {
627
0
    if (QDATA_QUARK (priv_data, i) == weak_ref_quark)
628
0
      QDATA_NOTIFY (priv_data, i) (QDATA_DATA (priv_data, i), obj);
629
0
    if (QDATA_DESTROY (priv_data, i))
630
0
      QDATA_DESTROY (priv_data, i) (QDATA_DATA (priv_data, i));
631
0
  }
632
7
  g_free (priv_data->qdata);
633
634
7
  if (priv_data->n_parents)
635
0
    g_warning ("%s: object finalizing but still has %d parents (object:%p)",
636
7
        G_STRFUNC, priv_data->n_parents, obj);
637
7
  g_free (priv_data->parents);
638
639
7
  g_free (priv_data);
640
7
}
641
642
/**
643
 * gst_mini_object_unref: (skip)
644
 * @mini_object: the mini-object
645
 *
646
 * Decreases the reference count of the mini-object, possibly freeing
647
 * the mini-object.
648
 */
649
void
650
gst_mini_object_unref (GstMiniObject * mini_object)
651
4.14M
{
652
4.14M
  gint old_refcount, new_refcount;
653
654
  /* FIXME: The GstBufferMapInfo clear function before 1.28 could call
655
   * gst_memory_unref() with NULL, and because we use macros/inline functions
656
   * all along the way to here this needs to be checked here. */
657
4.14M
  if (!mini_object)
658
0
    return;
659
660
4.14M
  g_return_if_fail (GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) > 0);
661
662
4.14M
  old_refcount = g_atomic_int_add (&mini_object->refcount, -1);
663
4.14M
  new_refcount = old_refcount - 1;
664
665
4.14M
  g_return_if_fail (old_refcount > 0);
666
667
4.14M
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p unref %d->%d",
668
4.14M
      mini_object, old_refcount, new_refcount);
669
670
4.14M
  GST_TRACER_MINI_OBJECT_UNREFFED (mini_object, new_refcount);
671
672
4.14M
  if (new_refcount == 0) {
673
1.64M
    gboolean do_free;
674
675
1.64M
    if (mini_object->dispose)
676
890k
      do_free = mini_object->dispose (mini_object);
677
757k
    else
678
757k
      do_free = TRUE;
679
680
    /* if the subclass recycled the object (and returned FALSE) we don't
681
     * want to free the instance anymore */
682
1.64M
    if (G_LIKELY (do_free)) {
683
      /* there should be no outstanding locks */
684
1.64M
      g_return_if_fail ((g_atomic_int_get (&mini_object->lockstate) & LOCK_MASK)
685
1.64M
          < 4);
686
687
1.64M
      free_priv_data (mini_object);
688
689
1.64M
      GST_TRACER_MINI_OBJECT_DESTROYED (mini_object);
690
1.64M
      if (mini_object->free)
691
1.64M
        mini_object->free (mini_object);
692
1.64M
    }
693
1.64M
  }
694
4.14M
}
695
696
/**
697
 * gst_clear_mini_object: (skip)
698
 * @object_ptr: a pointer to a #GstMiniObject reference
699
 *
700
 * Clears a reference to a #GstMiniObject.
701
 *
702
 * @object_ptr must not be %NULL.
703
 *
704
 * If the reference is %NULL then this function does nothing.
705
 * Otherwise, the reference count of the object is decreased using
706
 * gst_mini_object_unref() and the pointer is set to %NULL.
707
 *
708
 * A macro is also included that allows this function to be used without
709
 * pointer casts.
710
 *
711
 * Since: 1.16
712
 **/
713
#undef gst_clear_mini_object
714
void
715
gst_clear_mini_object (GstMiniObject ** object_ptr)
716
0
{
717
0
  g_clear_pointer (object_ptr, gst_mini_object_unref);
718
0
}
719
720
/**
721
 * gst_mini_object_replace:
722
 * @olddata: (inout) (transfer full) (nullable): pointer to a pointer to a
723
 *     mini-object to be replaced
724
 * @newdata: (allow-none): pointer to new mini-object
725
 *
726
 * Atomically modifies a pointer to point to a new mini-object.
727
 * The reference count of @olddata is decreased and the reference count of
728
 * @newdata is increased.
729
 *
730
 * Either @newdata and the value pointed to by @olddata may be %NULL.
731
 *
732
 * Returns: %TRUE if @newdata was different from @olddata
733
 */
734
gboolean
735
gst_mini_object_replace (GstMiniObject ** olddata, GstMiniObject * newdata)
736
294k
{
737
294k
  GstMiniObject *olddata_val;
738
739
294k
  g_return_val_if_fail (olddata != NULL, FALSE);
740
741
294k
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "replace %p (%d) with %p (%d)",
742
294k
      *olddata, *olddata ? (*olddata)->refcount : 0,
743
294k
      newdata, newdata ? newdata->refcount : 0);
744
745
294k
  olddata_val = (GstMiniObject *) g_atomic_pointer_get ((gpointer *) olddata);
746
747
294k
  if (G_UNLIKELY (olddata_val == newdata))
748
227k
    return FALSE;
749
750
67.1k
  if (newdata)
751
42.0k
    gst_mini_object_ref (newdata);
752
753
67.1k
  while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
754
67.1k
              olddata, (gpointer) olddata_val, newdata))) {
755
0
    olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
756
0
    if (G_UNLIKELY (olddata_val == newdata))
757
0
      break;
758
0
  }
759
760
67.1k
  if (olddata_val)
761
31.5k
    gst_mini_object_unref (olddata_val);
762
763
67.1k
  return olddata_val != newdata;
764
294k
}
765
766
/**
767
 * gst_mini_object_steal: (skip)
768
 * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
769
 *     be stolen
770
 *
771
 * Replace the current #GstMiniObject pointer to by @olddata with %NULL and
772
 * return the old value.
773
 *
774
 * Returns: (nullable): the #GstMiniObject at @oldata
775
 */
776
GstMiniObject *
777
gst_mini_object_steal (GstMiniObject ** olddata)
778
0
{
779
0
  GstMiniObject *olddata_val;
780
781
0
  g_return_val_if_fail (olddata != NULL, NULL);
782
783
0
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "steal %p (%d)",
784
0
      *olddata, *olddata ? (*olddata)->refcount : 0);
785
786
0
  do {
787
0
    olddata_val = (GstMiniObject *) g_atomic_pointer_get ((gpointer *) olddata);
788
0
    if (olddata_val == NULL)
789
0
      break;
790
0
  } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
791
0
              olddata, (gpointer) olddata_val, NULL)));
792
793
0
  return olddata_val;
794
0
}
795
796
/**
797
 * gst_mini_object_take:
798
 * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
799
 *     be replaced
800
 * @newdata: pointer to new mini-object
801
 *
802
 * Modifies a pointer to point to a new mini-object. The modification
803
 * is done atomically. This version is similar to gst_mini_object_replace()
804
 * except that it does not increase the refcount of @newdata and thus
805
 * takes ownership of @newdata.
806
 *
807
 * Either @newdata and the value pointed to by @olddata may be %NULL.
808
 *
809
 * Returns: %TRUE if @newdata was different from @olddata
810
 */
811
gboolean
812
gst_mini_object_take (GstMiniObject ** olddata, GstMiniObject * newdata)
813
31
{
814
31
  GstMiniObject *olddata_val;
815
816
31
  g_return_val_if_fail (olddata != NULL, FALSE);
817
818
31
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "take %p (%d) with %p (%d)",
819
31
      *olddata, *olddata ? (*olddata)->refcount : 0,
820
31
      newdata, newdata ? newdata->refcount : 0);
821
822
31
  do {
823
31
    olddata_val = (GstMiniObject *) g_atomic_pointer_get ((gpointer *) olddata);
824
31
    if (G_UNLIKELY (olddata_val == newdata))
825
31
      break;
826
31
  } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
827
31
              olddata, (gpointer) olddata_val, newdata)));
828
829
31
  if (olddata_val)
830
0
    gst_mini_object_unref (olddata_val);
831
832
31
  return olddata_val != newdata;
833
31
}
834
835
/**
836
 * gst_mini_object_weak_ref: (skip)
837
 * @object: #GstMiniObject to reference weakly
838
 * @notify: callback to invoke before the mini object is freed
839
 * @data: extra data to pass to notify
840
 *
841
 * Adds a weak reference callback to a mini object. Weak references are
842
 * used for notification when a mini object is finalized. They are called
843
 * "weak references" because they allow you to safely hold a pointer
844
 * to the mini object without calling gst_mini_object_ref()
845
 * (gst_mini_object_ref() adds a strong reference, that is, forces the object
846
 * to stay alive).
847
 */
848
void
849
gst_mini_object_weak_ref (GstMiniObject * object,
850
    GstMiniObjectNotify notify, gpointer data)
851
0
{
852
0
  g_return_if_fail (object != NULL);
853
0
  g_return_if_fail (notify != NULL);
854
0
  g_return_if_fail (GST_MINI_OBJECT_REFCOUNT_VALUE (object) >= 1);
855
856
0
  G_LOCK (qdata_mutex);
857
0
  set_notify (object, -1, weak_ref_quark, notify, data, NULL);
858
0
  G_UNLOCK (qdata_mutex);
859
0
}
860
861
/**
862
 * gst_mini_object_weak_unref: (skip)
863
 * @object: #GstMiniObject to remove a weak reference from
864
 * @notify: callback to search for
865
 * @data: data to search for
866
 *
867
 * Removes a weak reference callback from a mini object.
868
 */
869
void
870
gst_mini_object_weak_unref (GstMiniObject * object,
871
    GstMiniObjectNotify notify, gpointer data)
872
0
{
873
0
  gint i;
874
875
0
  g_return_if_fail (object != NULL);
876
0
  g_return_if_fail (notify != NULL);
877
878
0
  G_LOCK (qdata_mutex);
879
0
  if ((i = find_notify (object, weak_ref_quark, TRUE, notify, data)) != -1) {
880
0
    remove_notify (object, i);
881
0
  } else {
882
0
    g_warning ("%s: couldn't find weak ref %p (object:%p data:%p)", G_STRFUNC,
883
0
        notify, object, data);
884
0
  }
885
0
  G_UNLOCK (qdata_mutex);
886
0
}
887
888
/**
889
 * gst_mini_object_set_qdata:
890
 * @object: a #GstMiniObject
891
 * @quark: A #GQuark, naming the user data pointer
892
 * @data: An opaque user data pointer
893
 * @destroy: Function to invoke with @data as argument, when @data
894
 *           needs to be freed
895
 *
896
 * This sets an opaque, named pointer on a miniobject.
897
 * The name is specified through a #GQuark (retrieved e.g. via
898
 * g_quark_from_static_string()), and the pointer
899
 * can be gotten back from the @object with gst_mini_object_get_qdata()
900
 * until the @object is disposed.
901
 * Setting a previously set user data pointer, overrides (frees)
902
 * the old pointer set, using %NULL as pointer essentially
903
 * removes the data stored.
904
 *
905
 * @destroy may be specified which is called with @data as argument
906
 * when the @object is disposed, or the data is being overwritten by
907
 * a call to gst_mini_object_set_qdata() with the same @quark.
908
 */
909
void
910
gst_mini_object_set_qdata (GstMiniObject * object, GQuark quark,
911
    gpointer data, GDestroyNotify destroy)
912
0
{
913
0
  gint i;
914
0
  gpointer old_data = NULL;
915
0
  GDestroyNotify old_notify = NULL;
916
917
0
  g_return_if_fail (object != NULL);
918
0
  g_return_if_fail (quark > 0);
919
920
0
  G_LOCK (qdata_mutex);
921
0
  if ((i = find_notify (object, quark, FALSE, NULL, NULL)) != -1) {
922
0
    PrivData *priv_data = object->priv_pointer;
923
924
0
    old_data = QDATA_DATA (priv_data, i);
925
0
    old_notify = QDATA_DESTROY (priv_data, i);
926
927
0
    if (data == NULL)
928
0
      remove_notify (object, i);
929
0
  }
930
0
  if (data != NULL)
931
0
    set_notify (object, i, quark, NULL, data, destroy);
932
0
  G_UNLOCK (qdata_mutex);
933
934
0
  if (old_notify)
935
0
    old_notify (old_data);
936
0
}
937
938
/**
939
 * gst_mini_object_get_qdata:
940
 * @object: The GstMiniObject to get a stored user data pointer from
941
 * @quark: A #GQuark, naming the user data pointer
942
 *
943
 * This function gets back user data pointers stored via
944
 * gst_mini_object_set_qdata().
945
 *
946
 * Returns: (transfer none) (nullable): The user data pointer set, or
947
 * %NULL
948
 */
949
gpointer
950
gst_mini_object_get_qdata (GstMiniObject * object, GQuark quark)
951
0
{
952
0
  guint i;
953
0
  gpointer result;
954
955
0
  g_return_val_if_fail (object != NULL, NULL);
956
0
  g_return_val_if_fail (quark > 0, NULL);
957
958
0
  G_LOCK (qdata_mutex);
959
0
  if ((i = find_notify (object, quark, FALSE, NULL, NULL)) != -1) {
960
0
    PrivData *priv_data = object->priv_pointer;
961
0
    result = QDATA_DATA (priv_data, i);
962
0
  } else {
963
0
    result = NULL;
964
0
  }
965
0
  G_UNLOCK (qdata_mutex);
966
967
0
  return result;
968
0
}
969
970
/**
971
 * gst_mini_object_steal_qdata:
972
 * @object: The GstMiniObject to get a stored user data pointer from
973
 * @quark: A #GQuark, naming the user data pointer
974
 *
975
 * This function gets back user data pointers stored via gst_mini_object_set_qdata()
976
 * and removes the data from @object without invoking its `destroy()` function (if
977
 * any was set).
978
 *
979
 * Returns: (transfer full) (nullable): The user data pointer set, or
980
 * %NULL
981
 */
982
gpointer
983
gst_mini_object_steal_qdata (GstMiniObject * object, GQuark quark)
984
0
{
985
0
  guint i;
986
0
  gpointer result;
987
988
0
  g_return_val_if_fail (object != NULL, NULL);
989
0
  g_return_val_if_fail (quark > 0, NULL);
990
991
0
  G_LOCK (qdata_mutex);
992
0
  if ((i = find_notify (object, quark, FALSE, NULL, NULL)) != -1) {
993
0
    PrivData *priv_data = object->priv_pointer;
994
0
    result = QDATA_DATA (priv_data, i);
995
0
    remove_notify (object, i);
996
0
  } else {
997
0
    result = NULL;
998
0
  }
999
0
  G_UNLOCK (qdata_mutex);
1000
1001
0
  return result;
1002
0
}
1003
1004
/**
1005
 * gst_mini_object_add_parent:
1006
 * @object: a #GstMiniObject
1007
 * @parent: a parent #GstMiniObject
1008
 *
1009
 * This adds @parent as a parent for @object. Having one ore more parents affects the
1010
 * writability of @object: if a @parent is not writable, @object is also not
1011
 * writable, regardless of its refcount. @object is only writable if all
1012
 * the parents are writable and its own refcount is exactly 1.
1013
 *
1014
 * Note: This function does not take ownership of @parent and also does not
1015
 * take an additional reference. It is the responsibility of the caller to
1016
 * remove the parent again at a later time.
1017
 *
1018
 * Since: 1.16
1019
 */
1020
void
1021
gst_mini_object_add_parent (GstMiniObject * object, GstMiniObject * parent)
1022
293k
{
1023
293k
  gint priv_state;
1024
1025
293k
  g_return_if_fail (object != NULL);
1026
1027
293k
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "adding parent %p to object %p", parent,
1028
293k
      object);
1029
1030
293k
  priv_state = lock_priv_pointer (object);
1031
  /* If we already had one parent, we need to allocate the full struct now */
1032
293k
  if (priv_state == PRIV_DATA_STATE_ONE_PARENT) {
1033
    /* Unlock again */
1034
9
    g_atomic_int_set ((gint *) & object->priv_uint, priv_state);
1035
1036
9
    ensure_priv_data (object);
1037
9
    priv_state = PRIV_DATA_STATE_PARENTS_OR_QDATA;
1038
9
  }
1039
1040
  /* Now we either have to add the new parent to the full struct, or add
1041
   * our one and only parent to the pointer field */
1042
293k
  if (priv_state == PRIV_DATA_STATE_PARENTS_OR_QDATA) {
1043
1.63k
    PrivData *priv_data = object->priv_pointer;
1044
1045
    /* Lock parents */
1046
1.63k
    while (!g_atomic_int_compare_and_exchange (&priv_data->parent_lock, 0, 1));
1047
1048
1.63k
    if (priv_data->n_parents >= priv_data->n_parents_len) {
1049
0
      priv_data->n_parents_len *= 2;
1050
0
      if (priv_data->n_parents_len == 0)
1051
0
        priv_data->n_parents_len = 16;
1052
1053
0
      priv_data->parents =
1054
0
          g_realloc (priv_data->parents,
1055
0
          priv_data->n_parents_len * sizeof (GstMiniObject *));
1056
0
    }
1057
1.63k
    priv_data->parents[priv_data->n_parents] = parent;
1058
1.63k
    priv_data->n_parents++;
1059
1060
    /* Unlock again */
1061
1.63k
    g_atomic_int_set (&priv_data->parent_lock, 0);
1062
291k
  } else if (priv_state == PRIV_DATA_STATE_NO_PARENT) {
1063
291k
    object->priv_pointer = parent;
1064
1065
    /* Unlock again */
1066
291k
    g_atomic_int_set ((gint *) & object->priv_uint, PRIV_DATA_STATE_ONE_PARENT);
1067
291k
  } else {
1068
0
    g_assert_not_reached ();
1069
0
  }
1070
293k
}
1071
1072
/**
1073
 * gst_mini_object_remove_parent:
1074
 * @object: a #GstMiniObject
1075
 * @parent: a parent #GstMiniObject
1076
 *
1077
 * This removes @parent as a parent for @object. See
1078
 * gst_mini_object_add_parent().
1079
 *
1080
 * Since: 1.16
1081
 */
1082
void
1083
gst_mini_object_remove_parent (GstMiniObject * object, GstMiniObject * parent)
1084
293k
{
1085
293k
  gint priv_state;
1086
1087
293k
  g_return_if_fail (object != NULL);
1088
1089
293k
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "removing parent %p from object %p",
1090
293k
      parent, object);
1091
1092
293k
  priv_state = lock_priv_pointer (object);
1093
1094
  /* Now we either have to add the new parent to the full struct, or add
1095
   * our one and only parent to the pointer field */
1096
293k
  if (priv_state == PRIV_DATA_STATE_PARENTS_OR_QDATA) {
1097
1.64k
    PrivData *priv_data = object->priv_pointer;
1098
1.64k
    guint i;
1099
1100
    /* Lock parents */
1101
1.64k
    while (!g_atomic_int_compare_and_exchange (&priv_data->parent_lock, 0, 1));
1102
1103
2.65k
    for (i = 0; i < priv_data->n_parents; i++)
1104
2.65k
      if (parent == priv_data->parents[i])
1105
1.64k
        break;
1106
1107
1.64k
    if (i != priv_data->n_parents) {
1108
1.64k
      priv_data->n_parents--;
1109
1.64k
      if (priv_data->n_parents != i)
1110
11
        priv_data->parents[i] = priv_data->parents[priv_data->n_parents];
1111
1.64k
    } else {
1112
0
      g_warning ("%s: couldn't find parent %p (object:%p)", G_STRFUNC,
1113
0
          object, parent);
1114
0
    }
1115
1116
    /* Unlock again */
1117
1.64k
    g_atomic_int_set (&priv_data->parent_lock, 0);
1118
291k
  } else if (priv_state == PRIV_DATA_STATE_ONE_PARENT) {
1119
291k
    if (object->priv_pointer != parent) {
1120
0
      g_warning ("%s: couldn't find parent %p (object:%p)", G_STRFUNC,
1121
0
          object, parent);
1122
      /* Unlock again */
1123
0
      g_atomic_int_set ((gint *) & object->priv_uint, priv_state);
1124
291k
    } else {
1125
291k
      object->priv_pointer = NULL;
1126
      /* Unlock again */
1127
291k
      g_atomic_int_set ((gint *) & object->priv_uint,
1128
291k
          PRIV_DATA_STATE_NO_PARENT);
1129
291k
    }
1130
291k
  } else {
1131
    /* Unlock again */
1132
0
    g_atomic_int_set ((gint *) & object->priv_uint, PRIV_DATA_STATE_NO_PARENT);
1133
0
  }
1134
293k
}