summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2013-04-29 13:04:11 -0400
committerDan Winship <danw@gnome.org>2013-10-22 11:01:15 -0400
commit0d62eb467f8fbf7a59454bec3498bcdd3c7889e0 (patch)
tree1fec1523c7aaf8e2209c8dc8eaabe1efc86404a4
parenta7bd6c47db8330fa0b88201ff4d9abf72fb10ef5 (diff)
downloadglib-0d62eb467f8fbf7a59454bec3498bcdd3c7889e0.tar.gz
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
-rw-r--r--gobject/gobject.c26
-rw-r--r--gobject/tests/.gitignore1
-rw-r--r--gobject/tests/Makefile.am1
-rw-r--r--gobject/tests/object.c151
4 files changed, 169 insertions, 10 deletions
diff --git a/gobject/gobject.c b/gobject/gobject.c
index 96a9d4f31..9c58a5f62 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 e11cdb055..a3f8db3fe 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 10c433db4..2c5cc1a41 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 000000000..63c85a88a
--- /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 ();
+}