mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-23 01:18:53 +02:00
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
This commit is contained in:
@@ -950,6 +950,16 @@ g_object_interface_list_properties (gpointer g_iface,
|
|||||||
return pspecs;
|
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
|
static void
|
||||||
g_object_init (GObject *object,
|
g_object_init (GObject *object,
|
||||||
GObjectClass *class)
|
GObjectClass *class)
|
||||||
@@ -1021,6 +1031,12 @@ g_object_real_dispose (GObject *object)
|
|||||||
static void
|
static void
|
||||||
g_object_finalize (GObject *object)
|
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);
|
g_datalist_clear (&object->qdata);
|
||||||
|
|
||||||
#ifdef G_ENABLE_DEBUG
|
#ifdef G_ENABLE_DEBUG
|
||||||
@@ -1584,16 +1600,6 @@ slist_maybe_remove (GSList **slist,
|
|||||||
return FALSE;
|
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
|
static gpointer
|
||||||
g_object_new_with_custom_constructor (GObjectClass *class,
|
g_object_new_with_custom_constructor (GObjectClass *class,
|
||||||
GObjectConstructParam *params,
|
GObjectConstructParam *params,
|
||||||
|
1
gobject/tests/.gitignore
vendored
1
gobject/tests/.gitignore
vendored
@@ -4,6 +4,7 @@ closure
|
|||||||
dynamictests
|
dynamictests
|
||||||
enums
|
enums
|
||||||
ifaceproperties
|
ifaceproperties
|
||||||
|
object
|
||||||
param
|
param
|
||||||
properties
|
properties
|
||||||
qdata
|
qdata
|
||||||
|
@@ -21,6 +21,7 @@ test_programs = \
|
|||||||
type \
|
type \
|
||||||
private \
|
private \
|
||||||
closure \
|
closure \
|
||||||
|
object \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
151
gobject/tests/object.c
Normal file
151
gobject/tests/object.c
Normal file
@@ -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 ();
|
||||||
|
}
|
Reference in New Issue
Block a user