gstminiobject.c 13.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* GStreamer
 * Copyright (C) 2005 David Schleef <ds@schleef.org>
 *
 * gstminiobject.h: Header for GstMiniObject
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
21 22 23 24
/**
 * SECTION:gstminiobject
 * @short_description: Lightweight base class for the GStreamer object hierarchy
 *
Wim Taymans's avatar
Wim Taymans committed
25 26
 * #GstMiniObject is a simple structure that can be used to implement refcounted
 * types.
Wim Taymans's avatar
Wim Taymans committed
27
 *
Wim Taymans's avatar
Wim Taymans committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
 * Subclasses will include #GstMiniObject as the first member in their structure
 * and then call gst_mini_object_init() to initialize the #GstMiniObject fields.
 *
 * gst_mini_object_ref() and gst_mini_object_unref() increment and decrement the
 * refcount respectively. When the refcount of a mini-object reaches 0, the
 * dispose function is called first and when this returns %TRUE, the free
 * function of the miniobject is called.
 *
 * A copy can be made with gst_mini_object_copy().
 *
 * gst_mini_object_is_writable() will return %TRUE when the refcount of the
 * object is exactly 1, meaning the current caller has the only reference to the
 * object. gst_mini_object_make_writable() will return a writable version of the
 * object, which might be a new copy when the refcount was not 1.
 *
 * Last reviewed on 2012-03-28 (0.11.3)
44
 */
45 46 47 48
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

49
#include "gst/gst_private.h"
50 51 52 53
#include "gst/gstminiobject.h"
#include "gst/gstinfo.h"
#include <gobject/gvaluecollector.h>

54 55
#ifndef GST_DISABLE_TRACE
#include "gsttrace.h"
56
static GstAllocTrace *_gst_mini_object_trace;
57 58
#endif

59 60
/* Mutex used for weak referencing */
G_LOCK_DEFINE_STATIC (weak_refs_mutex);
61

62 63 64 65
void
_priv_gst_mini_object_initialize (void)
{
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
66
  _gst_mini_object_trace = _gst_alloc_trace_register ("GstMiniObject", 0);
67 68 69
#endif
}

70
/**
71 72
 * gst_mini_object_init:
 * @mini_object: a #GstMiniObject 
73
 * @type: the #GType of the mini-object to create
74
 * @size: the size of the data
75
 *
76
 * Initializes a mini-object with the desired type and size.
77 78 79
 *
 * MT safe
 *
80
 * Returns: (transfer full): the new mini-object.
81
 */
82 83
void
gst_mini_object_init (GstMiniObject * mini_object, GType type, gsize size)
84
{
85 86
  mini_object->type = type;
  mini_object->refcount = 1;
Wim Taymans's avatar
Wim Taymans committed
87
  mini_object->flags = 0;
88
  mini_object->size = size;
89 90
  mini_object->n_weak_refs = 0;
  mini_object->weak_refs = NULL;
91 92

#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
93
  _gst_alloc_trace_new (_gst_mini_object_trace, mini_object);
94
#endif
95 96
}

97 98 99 100 101 102 103 104
/**
 * gst_mini_object_copy:
 * @mini_object: the mini-object to copy
 *
 * Creates a copy of the mini-object.
 *
 * MT safe
 *
105
 * Returns: (transfer full): the new mini-object.
106
 */
107 108 109
GstMiniObject *
gst_mini_object_copy (const GstMiniObject * mini_object)
{
110
  GstMiniObject *copy;
111

112 113
  g_return_val_if_fail (mini_object != NULL, NULL);

114
  if (mini_object->copy)
115
    copy = mini_object->copy (mini_object);
116 117
  else
    copy = NULL;
118

119
  return copy;
120 121
}

122 123 124 125 126
/**
 * gst_mini_object_is_writable:
 * @mini_object: the mini-object to check
 *
 * Checks if a mini-object is writable.  A mini-object is writable
Wim Taymans's avatar
Wim Taymans committed
127 128
 * if the reference count is one. Modification of a mini-object should
 * only be done after verifying that it is writable.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
129
 *
130 131 132 133
 * MT safe
 *
 * Returns: TRUE if the object is writable.
 */
134 135 136
gboolean
gst_mini_object_is_writable (const GstMiniObject * mini_object)
{
137 138
  g_return_val_if_fail (mini_object != NULL, FALSE);

139
  return (GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) == 1);
140 141
}

142 143
/**
 * gst_mini_object_make_writable:
144
 * @mini_object: (transfer full): the mini-object to make writable
145
 *
146
 * Checks if a mini-object is writable.  If not, a writable copy is made and
147 148
 * returned.  This gives away the reference to the original mini object,
 * and returns a reference to the new object.
149 150 151
 *
 * MT safe
 *
152 153
 * Returns: (transfer full): a mini-object (possibly the same pointer) that
 *     is writable.
154
 */
155
GstMiniObject *
156
gst_mini_object_make_writable (GstMiniObject * mini_object)
157
{
158 159
  GstMiniObject *ret;

160 161
  g_return_val_if_fail (mini_object != NULL, NULL);

162
  if (gst_mini_object_is_writable (mini_object)) {
Wim Taymans's avatar
Wim Taymans committed
163
    ret = mini_object;
164 165
  } else {
    ret = gst_mini_object_copy (mini_object);
166 167
    GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy %s miniobject %p -> %p",
        g_type_name (GST_MINI_OBJECT_TYPE (mini_object)), mini_object, ret);
Wim Taymans's avatar
Wim Taymans committed
168
    gst_mini_object_unref (mini_object);
169
  }
170 171

  return ret;
172 173
}

174 175 176 177 178 179
/**
 * gst_mini_object_ref:
 * @mini_object: the mini-object
 *
 * Increase the reference count of the mini-object.
 *
180
 * Note that the refcount affects the writeability
181
 * of @mini-object, see gst_mini_object_is_writable(). It is
182 183
 * important to note that keeping additional references to
 * GstMiniObject instances can potentially increase the number
184
 * of memcpy operations in a pipeline, especially if the miniobject
185 186
 * is a #GstBuffer.
 *
187
 * Returns: (transfer full): the mini-object.
188
 */
189 190 191 192
GstMiniObject *
gst_mini_object_ref (GstMiniObject * mini_object)
{
  g_return_val_if_fail (mini_object != NULL, NULL);
193 194 195
  /* we can't assert that the refcount > 0 since the _free functions
   * increments the refcount from 0 to 1 again to allow resurecting
   * the object
196 197
   g_return_val_if_fail (mini_object->refcount > 0, NULL);
   */
198

199
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p ref %d->%d", mini_object,
200 201 202
      GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object),
      GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) + 1);

203 204 205 206 207
  g_atomic_int_inc (&mini_object->refcount);

  return mini_object;
}

208
static void
209
weak_refs_notify (GstMiniObject * obj)
210 211 212
{
  guint i;

213 214 215
  for (i = 0; i < obj->n_weak_refs; i++)
    obj->weak_refs[i].notify (obj->weak_refs[i].data, obj);
  g_free (obj->weak_refs);
216 217
}

218 219 220 221 222 223 224
/**
 * gst_mini_object_unref:
 * @mini_object: the mini-object
 *
 * Decreases the reference count of the mini-object, possibly freeing
 * the mini-object.
 */
225 226 227
void
gst_mini_object_unref (GstMiniObject * mini_object)
{
Wim Taymans's avatar
Wim Taymans committed
228
  g_return_if_fail (mini_object != NULL);
229

230
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p unref %d->%d",
231 232 233 234
      mini_object,
      GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object),
      GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) - 1);

Wim Taymans's avatar
Wim Taymans committed
235 236
  g_return_if_fail (mini_object->refcount > 0);

237
  if (G_UNLIKELY (g_atomic_int_dec_and_test (&mini_object->refcount))) {
238
    gboolean do_free;
239 240

    if (mini_object->dispose)
241 242 243
      do_free = mini_object->dispose (mini_object);
    else
      do_free = TRUE;
244

245
    /* if the subclass recycled the object (and returned FALSE) we don't
246
     * want to free the instance anymore */
247
    if (G_LIKELY (do_free)) {
248 249 250
      /* The weak reference stack is freed in the notification function */
      if (mini_object->n_weak_refs)
        weak_refs_notify (mini_object);
251

252
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
253
      _gst_alloc_trace_free (_gst_mini_object_trace, mini_object);
254 255 256 257
#endif
      if (mini_object->free)
        mini_object->free (mini_object);
    }
258 259 260
  }
}

261 262
/**
 * gst_mini_object_replace:
263 264
 * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
 *     be replaced
265 266
 * @newdata: pointer to new mini-object
 *
267 268 269 270
 * Atomically modifies a pointer to point to a new mini-object.
 * The reference count of @olddata is decreased and the reference count of
 * @newdata is increased.
 *
271
 * Either @newdata and the value pointed to by @olddata may be NULL.
272 273
 *
 * Returns: TRUE if @newdata was different from @olddata
274
 */
275
gboolean
276 277 278 279
gst_mini_object_replace (GstMiniObject ** olddata, GstMiniObject * newdata)
{
  GstMiniObject *olddata_val;

280
  g_return_val_if_fail (olddata != NULL, FALSE);
281

282
  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "replace %p (%d) with %p (%d)",
283 284 285
      *olddata, *olddata ? (*olddata)->refcount : 0,
      newdata, newdata ? newdata->refcount : 0);

286 287
  olddata_val = g_atomic_pointer_get ((gpointer *) olddata);

288 289
  if (G_UNLIKELY (olddata_val == newdata))
    return FALSE;
290

291
  if (newdata)
292 293
    gst_mini_object_ref (newdata);

294 295
  while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
              olddata, olddata_val, newdata))) {
296
    olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
297 298
    if (G_UNLIKELY (olddata_val == newdata))
      break;
299
  }
300

301
  if (olddata_val)
302
    gst_mini_object_unref (olddata_val);
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340

  return olddata_val != newdata;
}

/**
 * gst_mini_object_steal:
 * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
 *     be stolen
 *
 * Replace the current #GstMiniObject pointer to by @olddata with NULL and
 * return the old value.
 *
 * Returns: the #GstMiniObject at @oldata
 */
GstMiniObject *
gst_mini_object_steal (GstMiniObject ** olddata)
{
  GstMiniObject *olddata_val;

  g_return_val_if_fail (olddata != NULL, NULL);

  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "steal %p (%d)",
      *olddata, *olddata ? (*olddata)->refcount : 0);

  do {
    olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
    if (olddata_val == NULL)
      break;
  } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
              olddata, olddata_val, NULL)));

  return olddata_val;
}

/**
 * gst_mini_object_take:
 * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
 *     be replaced
Wim Taymans's avatar
Wim Taymans committed
341
 * @newdata: pointer to new mini-object
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
 *
 * Modifies a pointer to point to a new mini-object. The modification
 * is done atomically. This version is similar to gst_mini_object_replace()
 * except that it does not increase the refcount of @newdata and thus
 * takes ownership of @newdata.
 *
 * Either @newdata and the value pointed to by @olddata may be NULL.
 *
 * Returns: TRUE if @newdata was different from @olddata
 */
gboolean
gst_mini_object_take (GstMiniObject ** olddata, GstMiniObject * newdata)
{
  GstMiniObject *olddata_val;

  g_return_val_if_fail (olddata != NULL, FALSE);

  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "take %p (%d) with %p (%d)",
      *olddata, *olddata ? (*olddata)->refcount : 0,
      newdata, newdata ? newdata->refcount : 0);

  do {
    olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
    if (G_UNLIKELY (olddata_val == newdata))
      break;
  } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
              olddata, olddata_val, newdata)));

  if (olddata_val)
    gst_mini_object_unref (olddata_val);

  return olddata_val != newdata;
374 375
}

376 377
/**
 * gst_mini_object_weak_ref: (skip)
378
 * @object: #GstMiniObject to reference weakly
379 380 381 382 383 384 385 386 387 388
 * @notify: callback to invoke before the mini object is freed
 * @data: extra data to pass to notify
 *
 * Adds a weak reference callback to a mini object. Weak references are
 * used for notification when a mini object is finalized. They are called
 * "weak references" because they allow you to safely hold a pointer
 * to the mini object without calling gst_mini_object_ref()
 * (gst_mini_object_ref() adds a strong reference, that is, forces the object
 * to stay alive).
 *
389
 * Since: 0.10.35
390 391 392 393 394 395 396
 */
void
gst_mini_object_weak_ref (GstMiniObject * object,
    GstMiniObjectWeakNotify notify, gpointer data)
{
  guint i;

397
  g_return_if_fail (object != NULL);
398 399 400 401 402
  g_return_if_fail (notify != NULL);
  g_return_if_fail (GST_MINI_OBJECT_REFCOUNT_VALUE (object) >= 1);

  G_LOCK (weak_refs_mutex);

403
  if (object->n_weak_refs) {
404
    /* Don't add the weak reference if it already exists. */
405 406 407
    for (i = 0; i < object->n_weak_refs; i++) {
      if (object->weak_refs[i].notify == notify &&
          object->weak_refs[i].data == data) {
408 409 410 411 412 413
        g_warning ("%s: Attempt to re-add existing weak ref %p(%p) failed.",
            G_STRFUNC, notify, data);
        goto found;
      }
    }

414 415 416
    i = object->n_weak_refs++;
    object->weak_refs =
        g_realloc (object->weak_refs, sizeof (object->weak_refs[0]) * i);
417
  } else {
418 419
    object->weak_refs = g_malloc0 (sizeof (object->weak_refs[0]));
    object->n_weak_refs = 1;
420 421
    i = 0;
  }
422 423
  object->weak_refs[i].notify = notify;
  object->weak_refs[i].data = data;
424 425 426 427 428 429
found:
  G_UNLOCK (weak_refs_mutex);
}

/**
 * gst_mini_object_weak_unref: (skip)
430
 * @object: #GstMiniObject to remove a weak reference from
431 432 433 434 435
 * @notify: callback to search for
 * @data: data to search for
 *
 * Removes a weak reference callback to a mini object.
 *
436
 * Since: 0.10.35
437 438 439 440 441 442 443
 */
void
gst_mini_object_weak_unref (GstMiniObject * object,
    GstMiniObjectWeakNotify notify, gpointer data)
{
  gboolean found_one = FALSE;

444
  g_return_if_fail (object != NULL);
445 446 447 448
  g_return_if_fail (notify != NULL);

  G_LOCK (weak_refs_mutex);

449
  if (object->n_weak_refs) {
450 451
    guint i;

452 453 454
    for (i = 0; i < object->n_weak_refs; i++)
      if (object->weak_refs[i].notify == notify &&
          object->weak_refs[i].data == data) {
455
        found_one = TRUE;
456 457 458
        object->n_weak_refs -= 1;
        if (i != object->n_weak_refs)
          object->weak_refs[i] = object->weak_refs[object->n_weak_refs];
459 460 461 462 463 464 465 466

        break;
      }
  }
  G_UNLOCK (weak_refs_mutex);
  if (!found_one)
    g_warning ("%s: couldn't find weak ref %p(%p)", G_STRFUNC, notify, data);
}