[glib] gobject: forbid finalization-during-construction



commit 0d62eb467f8fbf7a59454bec3498bcdd3c7889e0
Author: Dan Winship <danw gnome org>
Date:   Mon Apr 29 13:04:11 2013 -0400

    gobject: forbid finalization-during-construction
    
    If a constructor() implementation created an object but then unreffed
    it rather than returning it, that object would get left on the
    construction_objects list, which would cause problems later when that
    memory location got reused by another object.
    
    "Fix" this by making it fail intentionally, and add a test for it (and
    for the normal, working singleton case).
    
    https://bugzilla.gnome.org/show_bug.cgi?id=661576

 gobject/gobject.c         |   26 +++++---
 gobject/tests/.gitignore  |    1 +
 gobject/tests/Makefile.am |    1 +
 gobject/tests/object.c    |  151 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 169 insertions(+), 10 deletions(-)
---
diff --git a/gobject/gobject.c b/gobject/gobject.c
index 96a9d4f..9c58a5f 100644
--- a/gobject/gobject.c
+++ b/gobject/gobject.c
@@ -950,6 +950,16 @@ g_object_interface_list_properties (gpointer      g_iface,
   return pspecs;
 }
 
+static inline gboolean
+object_in_construction_list (GObject *object)
+{
+  gboolean in_construction;
+  G_LOCK (construction_mutex);
+  in_construction = g_slist_find (construction_objects, object) != NULL;
+  G_UNLOCK (construction_mutex);
+  return in_construction;
+}
+
 static void
 g_object_init (GObject         *object,
               GObjectClass     *class)
@@ -1021,6 +1031,12 @@ g_object_real_dispose (GObject *object)
 static void
 g_object_finalize (GObject *object)
 {
+  if (object_in_construction_list (object))
+    {
+      g_error ("object %s %p finalized while still in-construction",
+               G_OBJECT_TYPE_NAME (object), object);
+    }
+
   g_datalist_clear (&object->qdata);
   
 #ifdef G_ENABLE_DEBUG
@@ -1584,16 +1600,6 @@ slist_maybe_remove (GSList       **slist,
   return FALSE;
 }
 
-static inline gboolean
-object_in_construction_list (GObject *object)
-{
-  gboolean in_construction;
-  G_LOCK (construction_mutex);
-  in_construction = g_slist_find (construction_objects, object) != NULL;
-  G_UNLOCK (construction_mutex);
-  return in_construction;
-}
-
 static gpointer
 g_object_new_with_custom_constructor (GObjectClass          *class,
                                       GObjectConstructParam *params,
diff --git a/gobject/tests/.gitignore b/gobject/tests/.gitignore
index e11cdb0..a3f8db3 100644
--- a/gobject/tests/.gitignore
+++ b/gobject/tests/.gitignore
@@ -4,6 +4,7 @@ closure
 dynamictests
 enums
 ifaceproperties
+object
 param
 properties
 qdata
diff --git a/gobject/tests/Makefile.am b/gobject/tests/Makefile.am
index 10c433d..2c5cc1a 100644
--- a/gobject/tests/Makefile.am
+++ b/gobject/tests/Makefile.am
@@ -21,6 +21,7 @@ test_programs = \
        type                            \
        private                         \
        closure                         \
+       object                          \
        $(NULL)
 
 # -----------------------------------------------------------------------------
diff --git a/gobject/tests/object.c b/gobject/tests/object.c
new file mode 100644
index 0000000..63c85a8
--- /dev/null
+++ b/gobject/tests/object.c
@@ -0,0 +1,151 @@
+#include <glib-object.h>
+
+/* --------------------------------- */
+/* test_object_constructor_singleton */
+
+typedef GObject MySingletonObject;
+typedef GObjectClass MySingletonObjectClass;
+
+GType my_singleton_object_get_type (void);
+
+G_DEFINE_TYPE (MySingletonObject, my_singleton_object, G_TYPE_OBJECT)
+
+static MySingletonObject *singleton;
+
+static void
+my_singleton_object_init (MySingletonObject *obj)
+{
+}
+
+static GObject *
+my_singleton_object_constructor (GType                  type,
+                                 guint                  n_construct_properties,
+                                 GObjectConstructParam *construct_params)
+{
+  GObject *object;
+
+  if (singleton)
+    return g_object_ref (singleton);
+
+  object = G_OBJECT_CLASS (my_singleton_object_parent_class)->
+    constructor (type, n_construct_properties, construct_params);
+  singleton = (MySingletonObject *)object;
+
+  return object;
+}
+
+static void
+my_singleton_object_finalize (MySingletonObject *obj)
+{
+  singleton = NULL;
+
+  G_OBJECT_CLASS (my_singleton_object_parent_class)->finalize (obj);
+}
+
+static void
+my_singleton_object_class_init (MySingletonObjectClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructor = my_singleton_object_constructor;
+  object_class->finalize = my_singleton_object_finalize;
+}
+
+static void
+test_object_constructor_singleton (void)
+{
+  MySingletonObject *one, *two, *three;
+
+  one = g_object_new (my_singleton_object_get_type (), NULL);
+  g_assert_cmpint (G_OBJECT (one)->ref_count, ==, 1);
+
+  two = g_object_new (my_singleton_object_get_type (), NULL);
+  g_assert (one == two);
+  g_assert_cmpint (G_OBJECT (two)->ref_count, ==, 2);
+
+  three = g_object_new (my_singleton_object_get_type (), NULL);
+  g_assert (one == three);
+  g_assert_cmpint (G_OBJECT (three)->ref_count, ==, 3);
+
+  g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
+
+  g_object_unref (one);
+  g_assert (one != NULL);
+
+  g_object_unref (three);
+  g_object_unref (two);
+
+  g_assert (one == NULL);
+}
+
+/* ----------------------------------- */
+/* test_object_constructor_infanticide */
+
+typedef GObject MyInfanticideObject;
+typedef GObjectClass MyInfanticideObjectClass;
+
+GType my_infanticide_object_get_type (void);
+
+G_DEFINE_TYPE (MyInfanticideObject, my_infanticide_object, G_TYPE_OBJECT)
+
+static void
+my_infanticide_object_init (MyInfanticideObject *obj)
+{
+}
+
+static GObject *
+my_infanticide_object_constructor (GType                  type,
+                                   guint                  n_construct_properties,
+                                   GObjectConstructParam *construct_params)
+{
+  GObject *object;
+
+  object = G_OBJECT_CLASS (my_infanticide_object_parent_class)->
+    constructor (type, n_construct_properties, construct_params);
+
+  g_object_unref (object);
+  g_assert_not_reached ();
+
+  return NULL;
+}
+
+static void
+my_infanticide_object_class_init (MyInfanticideObjectClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructor = my_infanticide_object_constructor;
+}
+
+static void
+test_object_constructor_infanticide_subprocess (void)
+{
+  g_object_new (my_infanticide_object_get_type (), NULL);
+  g_assert_not_reached ();
+}
+
+static void
+test_object_constructor_infanticide (void)
+{
+  g_test_bug ("661576");
+
+  g_test_trap_subprocess ("/object/constructor/infanticide/subprocess", 0, 0);
+  g_test_trap_assert_failed ();
+  g_test_trap_assert_stderr ("*finalized while still in-construction*");
+  g_test_trap_assert_stderr_unmatched ("*reached*");
+}
+
+/* --------------------------------- */
+
+int
+main (int argc, char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_bug_base ("http://bugzilla.gnome.org/";);
+
+  g_test_add_func ("/object/constructor/singleton", test_object_constructor_singleton);
+  g_test_add_func ("/object/constructor/infanticide", test_object_constructor_infanticide);
+  g_test_add_func ("/object/constructor/infanticide/subprocess", 
test_object_constructor_infanticide_subprocess);
+
+  return g_test_run ();
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]