New function: g_clear_object()
authorRyan Lortie <desrt@desrt.ca>
Mon, 8 Nov 2010 21:42:32 +0000 (16:42 -0500)
committerRyan Lortie <desrt@desrt.ca>
Mon, 8 Nov 2010 23:21:51 +0000 (18:21 -0500)
By analogy to g_clear_error, takes a pass-by-reference GObject reference
and, if non-%NULL, unrefs it and sets it equal to %NULL.

Bug #620263.

docs/reference/gobject/gobject-sections.txt
gobject/gobject.c
gobject/gobject.h
gobject/gobject.symbols
gobject/tests/.gitignore
gobject/tests/Makefile.am
gobject/tests/reference.c [new file with mode: 0644]

index 6df42c8..07ef1de 100644 (file)
@@ -257,6 +257,7 @@ GParameter
 g_object_ref
 g_object_unref
 g_object_ref_sink
+g_clear_object
 GInitiallyUnowned
 GInitiallyUnownedClass
 G_TYPE_INITIALLY_UNOWNED
index 49eff4d..e6f19da 100644 (file)
@@ -2729,6 +2729,44 @@ g_object_unref (gpointer _object)
 }
 
 /**
+ * g_clear_object:
+ * @object_ptr: a pointer to a #GObject reference
+ *
+ * Clears a reference to a #GObject.
+ *
+ * @object_ptr must not be %NULL.
+ *
+ * If the reference is %NULL then this function does nothing.
+ * Otherwise, the reference count of the object is decreased and the
+ * pointer is set to %NULL.
+ *
+ * This function is threadsafe and modifies the pointer atomically,
+ * using memory barriers where needed.
+ *
+ * A macro is also included that allows this function to be used without
+ * pointer casts.
+ *
+ * Since: 2.28
+ **/
+#undef g_clear_object
+void
+g_clear_object (volatile GObject **object_ptr)
+{
+  gpointer *ptr = (gpointer) object_ptr;
+  gpointer old;
+
+  /* This is a little frustrating.
+   * Would be nice to have an atomic exchange (with no compare).
+   */
+  do
+    old = g_atomic_pointer_get (ptr);
+  while G_UNLIKELY (!g_atomic_pointer_compare_and_exchange (ptr, old, NULL));
+
+  if (old)
+    g_object_unref (old);
+}
+
+/**
  * g_object_get_qdata:
  * @object: The GObject to get a stored user data pointer from
  * @quark: A #GQuark, naming the user data pointer
index c969b8f..971f365 100644 (file)
@@ -562,6 +562,21 @@ G_STMT_START { \
 #define G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec) \
     G_OBJECT_WARN_INVALID_PSPEC ((object), "property", (property_id), (pspec))
 
+void    g_clear_object (volatile GObject **object_ptr);
+#define g_clear_object(object_ptr) \
+  G_STMT_START {                                                             \
+    /* Only one access, please */                                            \
+    gpointer *_p = (gpointer) (object_ptr);                                  \
+    gpointer _o;                                                             \
+                                                                             \
+    do                                                                       \
+      _o = g_atomic_pointer_get (_p);                                        \
+    while G_UNLIKELY (!g_atomic_pointer_compare_and_exchange (_p, _o, NULL));\
+                                                                             \
+    if (_o)                                                                  \
+      g_object_unref (_o);                                                   \
+  } G_STMT_END
+
 G_END_DECLS
 
 #endif /* __G_OBJECT_H__ */
index c80293f..9be4f2e 100644 (file)
@@ -186,6 +186,7 @@ g_value_get_object
 g_value_set_object
 g_value_dup_object
 g_value_take_object
+g_clear_object
 #ifndef G_DISABLE_DEPRECATED
 g_value_set_object_take_ownership
 g_object_compat_control
index ba9d6c8..cd58ce5 100644 (file)
@@ -1,4 +1,5 @@
 binding
 dynamictests
 properties
+reference
 threadtests
index 8b1394d..eec8c5e 100644 (file)
@@ -5,7 +5,7 @@ INCLUDES = -g $(gobject_INCLUDES) $(GLIB_DEBUG_FLAGS)
 noinst_PROGRAMS  = $(TEST_PROGS)
 libgobject_LDADD = ../libgobject-2.0.la $(top_builddir)/gthread/libgthread-2.0.la $(top_builddir)/glib/libglib-2.0.la
 
-TEST_PROGS             += threadtests dynamictests binding properties
+TEST_PROGS             += threadtests dynamictests binding properties reference
 threadtests_SOURCES    = threadtests.c
 threadtests_LDADD      = $(libgobject_LDADD)
 dynamictests_SOURCES   = dynamictests.c
@@ -14,3 +14,4 @@ binding_SOURCES               = binding.c
 binding_LDADD          = $(libgobject_LDADD)
 properties_SOURCES      = properties.c
 properties_LDADD        = $(libgobject_LDADD)
+reference_LDADD                = $(libgobject_LDADD)
diff --git a/gobject/tests/reference.c b/gobject/tests/reference.c
new file mode 100644 (file)
index 0000000..38354a1
--- /dev/null
@@ -0,0 +1,35 @@
+#include <glib-object.h>
+
+static void
+test_clear (void)
+{
+  GObject *o = NULL;
+  GObject *tmp;
+
+  g_clear_object (&o);
+  g_assert (o == NULL);
+
+  tmp = g_object_new (G_TYPE_OBJECT, NULL);
+  g_assert_cmpint (tmp->ref_count, ==, 1);
+  o = g_object_ref (tmp);
+  g_assert (o != NULL);
+
+  g_assert_cmpint (tmp->ref_count, ==, 2);
+  g_clear_object (&o);
+  g_assert_cmpint (tmp->ref_count, ==, 1);
+  g_assert (o == NULL);
+
+  g_object_unref (tmp);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_type_init ();
+
+  g_test_add_func ("/object/clear", test_clear);
+
+  return g_test_run ();
+}