mini-object: use ref/unref directly in boxed copy/free
[platform/upstream/gstreamer.git] / gst / gstminiobject.c
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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /**
22  * SECTION:gstminiobject
23  * @short_description: Lightweight base class for the GStreamer object hierarchy
24  *
25  * #GstMiniObject is a baseclass like #GObject, but has been stripped down of
26  * features to be fast and small.
27  * It offers sub-classing and ref-counting in the same way as #GObject does.
28  * It has no properties and no signal-support though.
29  *
30  * Last reviewed on 2005-11-23 (0.9.5)
31  */
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include "gst/gst_private.h"
37 #include "gst/gstminiobject.h"
38 #include "gst/gstinfo.h"
39 #include <gobject/gvaluecollector.h>
40
41 #define GST_DISABLE_TRACE
42
43 #ifndef GST_DISABLE_TRACE
44 #include "gsttrace.h"
45 static GstAllocTrace *_gst_mini_object_trace;
46 #endif
47
48 /* Mutex used for weak referencing */
49 G_LOCK_DEFINE_STATIC (weak_refs_mutex);
50
51 /**
52  * gst_mini_object_register:
53  * @name: name of the new boxed type
54  *
55  * This function creates a new G_TYPE_BOXED derived type id for a new boxed type
56  * with name @name. The default miniobject refcounting copy and free function
57  * are used for the boxed type.
58  *
59  * Returns: a new G_TYPE_BOXED derived type id for @name.
60  */
61 GType
62 gst_mini_object_register (const gchar * name)
63 {
64   GType type;
65
66   g_return_val_if_fail (name != NULL, 0);
67
68   type = g_boxed_type_register_static (name,
69       (GBoxedCopyFunc) gst_mini_object_ref,
70       (GBoxedFreeFunc) gst_mini_object_unref);
71
72   return type;
73 }
74
75 /**
76  * gst_mini_object_init:
77  * @mini_object: a #GstMiniObject 
78  * @type: the #GType of the mini-object to create
79  * @size: the size of the data
80  *
81  * Initializes a mini-object with the desired type and size.
82  *
83  * MT safe
84  *
85  * Returns: (transfer full): the new mini-object.
86  */
87 void
88 gst_mini_object_init (GstMiniObject * mini_object, GType type, gsize size)
89 {
90   mini_object->type = type;
91   mini_object->refcount = 1;
92   mini_object->flags = 0;
93   mini_object->size = size;
94   mini_object->n_weak_refs = 0;
95   mini_object->weak_refs = NULL;
96 }
97
98 /**
99  * gst_mini_object_copy:
100  * @mini_object: the mini-object to copy
101  *
102  * Creates a copy of the mini-object.
103  *
104  * MT safe
105  *
106  * Returns: (transfer full): the new mini-object.
107  */
108 GstMiniObject *
109 gst_mini_object_copy (const GstMiniObject * mini_object)
110 {
111   GstMiniObject *copy;
112
113   g_return_val_if_fail (mini_object != NULL, NULL);
114
115   if (mini_object->copy)
116     copy = mini_object->copy (mini_object);
117   else
118     copy = NULL;
119
120   return copy;
121 }
122
123 /**
124  * gst_mini_object_is_writable:
125  * @mini_object: the mini-object to check
126  *
127  * Checks if a mini-object is writable.  A mini-object is writable
128  * if the reference count is one and the #GST_MINI_OBJECT_FLAG_READONLY
129  * flag is not set.  Modification of a mini-object should only be
130  * done after verifying that it is writable.
131  *
132  * MT safe
133  *
134  * Returns: TRUE if the object is writable.
135  */
136 gboolean
137 gst_mini_object_is_writable (const GstMiniObject * mini_object)
138 {
139   g_return_val_if_fail (mini_object != NULL, FALSE);
140
141   return (GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) == 1);
142 }
143
144 /**
145  * gst_mini_object_make_writable:
146  * @mini_object: (transfer full): the mini-object to make writable
147  *
148  * Checks if a mini-object is writable.  If not, a writable copy is made and
149  * returned.  This gives away the reference to the original mini object,
150  * and returns a reference to the new object.
151  *
152  * MT safe
153  *
154  * Returns: (transfer full): a mini-object (possibly the same pointer) that
155  *     is writable.
156  */
157 GstMiniObject *
158 gst_mini_object_make_writable (GstMiniObject * mini_object)
159 {
160   GstMiniObject *ret;
161
162   g_return_val_if_fail (mini_object != NULL, NULL);
163
164   if (gst_mini_object_is_writable (mini_object)) {
165     ret = mini_object;
166   } else {
167     ret = gst_mini_object_copy (mini_object);
168     GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy %s miniobject %p -> %p",
169         g_type_name (GST_MINI_OBJECT_TYPE (mini_object)), mini_object, ret);
170     gst_mini_object_unref (mini_object);
171   }
172
173   return ret;
174 }
175
176 /**
177  * gst_mini_object_ref:
178  * @mini_object: the mini-object
179  *
180  * Increase the reference count of the mini-object.
181  *
182  * Note that the refcount affects the writeability
183  * of @mini-object, see gst_mini_object_is_writable(). It is
184  * important to note that keeping additional references to
185  * GstMiniObject instances can potentially increase the number
186  * of memcpy operations in a pipeline, especially if the miniobject
187  * is a #GstBuffer.
188  *
189  * Returns: (transfer full): the mini-object.
190  */
191 GstMiniObject *
192 gst_mini_object_ref (GstMiniObject * mini_object)
193 {
194   g_return_val_if_fail (mini_object != NULL, NULL);
195   /* we can't assert that the refcount > 0 since the _free functions
196    * increments the refcount from 0 to 1 again to allow resurecting
197    * the object
198    g_return_val_if_fail (mini_object->refcount > 0, NULL);
199    */
200
201   GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p ref %d->%d", mini_object,
202       GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object),
203       GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) + 1);
204
205   g_atomic_int_inc (&mini_object->refcount);
206
207   return mini_object;
208 }
209
210 static void
211 weak_refs_notify (GstMiniObject * obj)
212 {
213   guint i;
214
215   for (i = 0; i < obj->n_weak_refs; i++)
216     obj->weak_refs[i].notify (obj->weak_refs[i].data, obj);
217   g_free (obj->weak_refs);
218 }
219
220 /**
221  * gst_mini_object_unref:
222  * @mini_object: the mini-object
223  *
224  * Decreases the reference count of the mini-object, possibly freeing
225  * the mini-object.
226  */
227 void
228 gst_mini_object_unref (GstMiniObject * mini_object)
229 {
230   g_return_if_fail (mini_object != NULL);
231   g_return_if_fail (mini_object->refcount > 0);
232
233   GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p unref %d->%d",
234       mini_object,
235       GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object),
236       GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) - 1);
237
238   if (G_UNLIKELY (g_atomic_int_dec_and_test (&mini_object->refcount))) {
239     gboolean do_free;
240
241     if (mini_object->dispose)
242       do_free = mini_object->dispose (mini_object);
243     else
244       do_free = TRUE;
245
246     /* if the subclass recycled the object (and returned FALSE) we don't
247      * want to free the instance anymore */
248     if (G_LIKELY (do_free)) {
249       /* The weak reference stack is freed in the notification function */
250       if (mini_object->n_weak_refs)
251         weak_refs_notify (mini_object);
252
253 #ifndef GST_DISABLE_TRACE
254       gst_alloc_trace_free (_gst_mini_object_trace, mini_object);
255 #endif
256       if (mini_object->free)
257         mini_object->free (mini_object);
258     }
259   }
260 }
261
262 /**
263  * gst_mini_object_replace:
264  * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
265  *     be replaced
266  * @newdata: pointer to new mini-object
267  *
268  * Atomically modifies a pointer to point to a new mini-object.
269  * The reference count of @olddata is decreased and the reference count of
270  * @newdata is increased.
271  *
272  * Either @newdata and the value pointed to by @olddata may be NULL.
273  *
274  * Returns: TRUE if @newdata was different from @olddata
275  */
276 gboolean
277 gst_mini_object_replace (GstMiniObject ** olddata, GstMiniObject * newdata)
278 {
279   GstMiniObject *olddata_val;
280
281   g_return_val_if_fail (olddata != NULL, FALSE);
282
283   GST_CAT_TRACE (GST_CAT_REFCOUNTING, "replace %p (%d) with %p (%d)",
284       *olddata, *olddata ? (*olddata)->refcount : 0,
285       newdata, newdata ? newdata->refcount : 0);
286
287   olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
288
289   if (G_UNLIKELY (olddata_val == newdata))
290     return FALSE;
291
292   if (newdata)
293     gst_mini_object_ref (newdata);
294
295   while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
296               olddata, olddata_val, newdata))) {
297     olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
298     if (G_UNLIKELY (olddata_val == newdata))
299       break;
300   }
301
302   if (olddata_val)
303     gst_mini_object_unref (olddata_val);
304
305   return olddata_val != newdata;
306 }
307
308 /**
309  * gst_mini_object_steal:
310  * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
311  *     be stolen
312  *
313  * Replace the current #GstMiniObject pointer to by @olddata with NULL and
314  * return the old value.
315  *
316  * Returns: the #GstMiniObject at @oldata
317  */
318 GstMiniObject *
319 gst_mini_object_steal (GstMiniObject ** olddata)
320 {
321   GstMiniObject *olddata_val;
322
323   g_return_val_if_fail (olddata != NULL, NULL);
324
325   GST_CAT_TRACE (GST_CAT_REFCOUNTING, "steal %p (%d)",
326       *olddata, *olddata ? (*olddata)->refcount : 0);
327
328   do {
329     olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
330     if (olddata_val == NULL)
331       break;
332   } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
333               olddata, olddata_val, NULL)));
334
335   return olddata_val;
336 }
337
338 /**
339  * gst_mini_object_take:
340  * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to
341  *     be replaced
342  *
343  * Modifies a pointer to point to a new mini-object. The modification
344  * is done atomically. This version is similar to gst_mini_object_replace()
345  * except that it does not increase the refcount of @newdata and thus
346  * takes ownership of @newdata.
347  *
348  * Either @newdata and the value pointed to by @olddata may be NULL.
349  *
350  * Returns: TRUE if @newdata was different from @olddata
351  */
352 gboolean
353 gst_mini_object_take (GstMiniObject ** olddata, GstMiniObject * newdata)
354 {
355   GstMiniObject *olddata_val;
356
357   g_return_val_if_fail (olddata != NULL, FALSE);
358
359   GST_CAT_TRACE (GST_CAT_REFCOUNTING, "take %p (%d) with %p (%d)",
360       *olddata, *olddata ? (*olddata)->refcount : 0,
361       newdata, newdata ? newdata->refcount : 0);
362
363   do {
364     olddata_val = g_atomic_pointer_get ((gpointer *) olddata);
365     if (G_UNLIKELY (olddata_val == newdata))
366       break;
367   } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
368               olddata, olddata_val, newdata)));
369
370   if (olddata_val)
371     gst_mini_object_unref (olddata_val);
372
373   return olddata_val != newdata;
374 }
375
376 /**
377  * gst_mini_object_weak_ref: (skip)
378  * @object: #GstMiniObject to reference weakly
379  * @notify: callback to invoke before the mini object is freed
380  * @data: extra data to pass to notify
381  *
382  * Adds a weak reference callback to a mini object. Weak references are
383  * used for notification when a mini object is finalized. They are called
384  * "weak references" because they allow you to safely hold a pointer
385  * to the mini object without calling gst_mini_object_ref()
386  * (gst_mini_object_ref() adds a strong reference, that is, forces the object
387  * to stay alive).
388  *
389  * Since: 0.10.35
390  */
391 void
392 gst_mini_object_weak_ref (GstMiniObject * object,
393     GstMiniObjectWeakNotify notify, gpointer data)
394 {
395   guint i;
396
397   g_return_if_fail (object != NULL);
398   g_return_if_fail (notify != NULL);
399   g_return_if_fail (GST_MINI_OBJECT_REFCOUNT_VALUE (object) >= 1);
400
401   G_LOCK (weak_refs_mutex);
402
403   if (object->n_weak_refs) {
404     /* Don't add the weak reference if it already exists. */
405     for (i = 0; i < object->n_weak_refs; i++) {
406       if (object->weak_refs[i].notify == notify &&
407           object->weak_refs[i].data == data) {
408         g_warning ("%s: Attempt to re-add existing weak ref %p(%p) failed.",
409             G_STRFUNC, notify, data);
410         goto found;
411       }
412     }
413
414     i = object->n_weak_refs++;
415     object->weak_refs =
416         g_realloc (object->weak_refs, sizeof (object->weak_refs[0]) * i);
417   } else {
418     object->weak_refs = g_malloc0 (sizeof (object->weak_refs[0]));
419     object->n_weak_refs = 1;
420     i = 0;
421   }
422   object->weak_refs[i].notify = notify;
423   object->weak_refs[i].data = data;
424 found:
425   G_UNLOCK (weak_refs_mutex);
426 }
427
428 /**
429  * gst_mini_object_weak_unref: (skip)
430  * @object: #GstMiniObject to remove a weak reference from
431  * @notify: callback to search for
432  * @data: data to search for
433  *
434  * Removes a weak reference callback to a mini object.
435  *
436  * Since: 0.10.35
437  */
438 void
439 gst_mini_object_weak_unref (GstMiniObject * object,
440     GstMiniObjectWeakNotify notify, gpointer data)
441 {
442   gboolean found_one = FALSE;
443
444   g_return_if_fail (object != NULL);
445   g_return_if_fail (notify != NULL);
446
447   G_LOCK (weak_refs_mutex);
448
449   if (object->n_weak_refs) {
450     guint i;
451
452     for (i = 0; i < object->n_weak_refs; i++)
453       if (object->weak_refs[i].notify == notify &&
454           object->weak_refs[i].data == data) {
455         found_one = TRUE;
456         object->n_weak_refs -= 1;
457         if (i != object->n_weak_refs)
458           object->weak_refs[i] = object->weak_refs[object->n_weak_refs];
459
460         break;
461       }
462   }
463   G_UNLOCK (weak_refs_mutex);
464   if (!found_one)
465     g_warning ("%s: couldn't find weak ref %p(%p)", G_STRFUNC, notify, data);
466 }