diff options
author | Dan Winship <danw@gnome.org> | 2013-04-29 13:04:11 -0400 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2013-10-22 11:01:15 -0400 |
commit | 0d62eb467f8fbf7a59454bec3498bcdd3c7889e0 (patch) | |
tree | 1fec1523c7aaf8e2209c8dc8eaabe1efc86404a4 | |
parent | a7bd6c47db8330fa0b88201ff4d9abf72fb10ef5 (diff) | |
download | glib-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.c | 26 | ||||
-rw-r--r-- | gobject/tests/.gitignore | 1 | ||||
-rw-r--r-- | gobject/tests/Makefile.am | 1 | ||||
-rw-r--r-- | gobject/tests/object.c | 151 |
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 (); +} |