diff --git a/gobject/ChangeLog b/gobject/ChangeLog index 1316d135a..6ee7a1990 100644 --- a/gobject/ChangeLog +++ b/gobject/ChangeLog @@ -1,3 +1,10 @@ +Thu Feb 27 17:33:19 2003 Owen Taylor + + * gtype.[ch] testgobject.c: Add support for instance-private data. + g_type_class_add_private(), g_type_instance_get_private(), + G_TYPE_INSTANCE_GET_PRIVATE(). (#101959, patch partly by + Mark McLoughlin, extensive feedback from Tim Janik.) + 2003-03-06 Matthias Clasen * gsignal.c (g_signal_handlers_block_matched): diff --git a/gobject/gtype.c b/gobject/gtype.c index 19cb39990..f8988323d 100644 --- a/gobject/gtype.c +++ b/gobject/gtype.c @@ -107,6 +107,18 @@ static GStaticRWLock type_rw_lock = G_STATIC_RW_LOCK_INIT; sizeof (gpointer)), \ sizeof (glong))) +/* The 2*sizeof(size_t) alignment here is borrowed from + * GNU libc, so it should be good most everywhere. + * It is more conservative than is needed on some 64-bit + * platforms, but ia64 does require a 16-byte alignment. + * The SIMD extensions for x86 and ppc32 would want a + * larger alignment than this, but we don't need to + * do better than malloc. + */ +#define STRUCT_ALIGNMENT (2 * sizeof (gsize)) +#define ALIGN_STRUCT(offset) \ + ((offset + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT) + /* --- typedefs --- */ typedef struct _TypeNode TypeNode; @@ -182,6 +194,10 @@ struct _TypeNode #define iface_node_set_dependants_array_W(n,d) (type_set_qdata_W ((n), static_quark_dependants_array, (d))) #define TYPE_ID_MASK ((GType) ((1 << G_TYPE_FUNDAMENTAL_SHIFT) - 1)) +#define NODE_IS_ANCESTOR(ancestor, node) \ + ((ancestor)->n_supers <= (node)->n_supers && \ + (node)->supers[(node)->n_supers - (ancestor)->n_supers] == NODE_TYPE (ancestor)) + struct _IFaceHolder { @@ -229,6 +245,7 @@ struct _InstanceData gconstpointer class_data; gpointer class; guint16 instance_size; + guint16 private_size; guint16 n_preallocs; GInstanceInitFunc instance_init; GMemChunk *mem_chunk; @@ -909,6 +926,13 @@ type_data_make_W (TypeNode *node, data->instance.class_data = info->class_data; data->instance.class = NULL; data->instance.instance_size = info->instance_size; + if (NODE_PARENT_TYPE (node)) + { + TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node)); + data->instance.private_size = pnode->data->instance.private_size; + } + else + data->instance.private_size = 0; #ifdef DISABLE_MEM_POOLS data->instance.n_preallocs = 0; #else /* !DISABLE_MEM_POOLS */ @@ -1338,6 +1362,19 @@ type_iface_blow_holder_info_Wm (TypeNode *iface, } } +/* Assumes type's class already exists + */ +static inline size_t +type_total_instance_size_I (TypeNode *node) +{ + gsize total_instance_size; + + total_instance_size = node->data->instance.instance_size; + if (node->data->instance.private_size != 0) + total_instance_size = ALIGN_STRUCT (total_instance_size) + node->data->instance.private_size; + + return total_instance_size; +} /* --- type structure creation/destruction --- */ GTypeInstance* @@ -1347,6 +1384,7 @@ g_type_create_instance (GType type) GTypeInstance *instance; GTypeClass *class; guint i; + gsize total_instance_size; node = lookup_type_node_I (type); if (!node || !node->is_instantiatable) @@ -1364,21 +1402,35 @@ g_type_create_instance (GType type) } class = g_type_class_ref (type); + + total_instance_size = type_total_instance_size_I (node); if (node->data->instance.n_preallocs) { G_WRITE_LOCK (&type_rw_lock); if (!node->data->instance.mem_chunk) - node->data->instance.mem_chunk = g_mem_chunk_new (NODE_NAME (node), - node->data->instance.instance_size, - (node->data->instance.instance_size * - node->data->instance.n_preallocs), - G_ALLOC_AND_FREE); + { + /* If there isn't private data, the compiler will have already + * added the necessary padding, but in the private data case, we + * have to pad ourselves to ensure proper alignment of all the + * atoms in the slab. + */ + gsize atom_size = total_instance_size; + if (node->data->instance.private_size) + atom_size = ALIGN_STRUCT (atom_size); + + node->data->instance.mem_chunk = g_mem_chunk_new (NODE_NAME (node), + atom_size, + (atom_size * + node->data->instance.n_preallocs), + G_ALLOC_AND_FREE); + } + instance = g_chunk_new0 (GTypeInstance, node->data->instance.mem_chunk); G_WRITE_UNLOCK (&type_rw_lock); } else - instance = g_malloc0 (node->data->instance.instance_size); /* fine without read lock */ + instance = g_malloc0 (total_instance_size); /* fine without read lock */ for (i = node->n_supers; i > 0; i--) { TypeNode *pnode; @@ -1424,7 +1476,7 @@ g_type_free_instance (GTypeInstance *instance) instance->g_class = NULL; #ifdef G_ENABLE_DEBUG - memset (instance, 0xaa, node->data->instance.instance_size); /* debugging hack */ + memset (instance, 0xaa, type_total_instance_size_I (node)); /* debugging hack */ #endif if (node->data->instance.n_preallocs) { @@ -1527,7 +1579,7 @@ type_class_init_Wm (TypeNode *node, g_assert (node->is_classed && node->data && node->data->class.class_size && !node->data->class.class); - + class = g_malloc0 (node->data->class.class_size); node->data->class.class = class; @@ -2255,8 +2307,7 @@ type_node_check_conformities_UorL (TypeNode *node, gboolean match; if (/* support_inheritance && */ - iface_node->n_supers <= node->n_supers && - node->supers[node->n_supers - iface_node->n_supers] == NODE_TYPE (iface_node)) + NODE_IS_ANCESTOR (iface_node, node)) return TRUE; support_interfaces = support_interfaces && node->is_instantiatable && NODE_IS_IFACE (iface_node); @@ -3025,3 +3076,83 @@ g_type_init (void) { g_type_init_with_debug_flags (0); } + +void +g_type_class_add_private (gpointer g_class, + gsize private_size) +{ + GType instance_type = ((GTypeClass *)g_class)->g_type; + TypeNode *node = lookup_type_node_I (instance_type); + gsize offset; + + if (!node || !node->is_instantiatable || !node->data || node->data->class.class != g_class) + { + g_warning ("cannot add private field to invalid (non-instantiatable) type '%s'", + type_descriptive_name_I (instance_type)); + return; + } + + if (NODE_PARENT_TYPE (node)) + { + TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node)); + if (node->data->instance.private_size != pnode->data->instance.private_size) + { + g_warning ("g_type_add_private() called multiple times for the same type"); + return; + } + } + + G_WRITE_LOCK (&type_rw_lock); + + offset = ALIGN_STRUCT (node->data->instance.private_size); + node->data->instance.private_size = offset + private_size; + + G_WRITE_UNLOCK (&type_rw_lock); +} + +gpointer +g_type_instance_get_private (GTypeInstance *instance, + GType private_type) +{ + TypeNode *instance_node; + TypeNode *private_node; + TypeNode *parent_node; + gsize offset; + + g_return_val_if_fail (instance != NULL && instance->g_class != NULL, NULL); + + instance_node = lookup_type_node_I (instance->g_class->g_type); + if (G_UNLIKELY (!instance_node || !instance_node->is_instantiatable)) + { + g_warning ("instance of invalid non-instantiatable type `%s'", + type_descriptive_name_I (instance->g_class->g_type)); + return NULL; + } + + private_node = lookup_type_node_I (private_type); + if (G_UNLIKELY (!private_node || !NODE_IS_ANCESTOR (private_node, instance_node))) + { + g_warning ("attempt to retrieve private data for invalid type '%s'", + type_descriptive_name_I (private_type)); + return NULL; + } + + /* Note that we don't need a read lock, since instance existing + * means that the instance class and all parent classes + * exist, so the node->data, node->data->instance.instance_size, + * and node->data->instance.private_size are not going to be changed. + * for any of the relevant types. + */ + + offset = ALIGN_STRUCT (instance_node->data->instance.instance_size); + + if (NODE_PARENT_TYPE (private_node)) + { + parent_node = lookup_type_node_I (NODE_PARENT_TYPE (private_node)); + g_assert (parent_node->data && parent_node->data->common.ref_count); + + offset += ALIGN_STRUCT (parent_node->data->instance.private_size); + } + + return G_STRUCT_MEMBER_P (instance, offset); +} diff --git a/gobject/gtype.h b/gobject/gtype.h index 611209f51..5aff33eff 100644 --- a/gobject/gtype.h +++ b/gobject/gtype.h @@ -150,6 +150,8 @@ struct _GTypeQuery #define G_TYPE_FROM_CLASS(g_class) (((GTypeClass*) (g_class))->g_type) #define G_TYPE_FROM_INTERFACE(g_iface) (((GTypeInterface*) (g_iface))->g_type) +#define G_TYPE_INSTANCE_GET_PRIVATE(instance, g_type, c_type) ((c_type*) g_type_instance_get_private ((GTypeInstance*) (instance), (g_type))) + /* debug flags for g_type_init_with_debug_flags() */ typedef enum /*< skip >*/ @@ -298,6 +300,10 @@ void g_type_interface_add_prerequisite (GType interface_type, GType *g_type_interface_prerequisites (GType interface_type, guint *n_prerequisites); +void g_type_class_add_private (gpointer g_class, + gsize private_size); +gpointer g_type_instance_get_private (GTypeInstance *instance, + GType private_type); /* --- protected (for fundamental type implementations) --- */ GTypePlugin* g_type_get_plugin (GType type); diff --git a/gobject/testgobject.c b/gobject/testgobject.c index 479959bf6..87c3bda7b 100644 --- a/gobject/testgobject.c +++ b/gobject/testgobject.c @@ -126,8 +126,10 @@ iface_print_string (TestIface *tiobj, #define TEST_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TEST_TYPE_OBJECT)) #define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT)) #define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass)) -typedef struct _TestObject TestObject; -typedef struct _TestObjectClass TestObjectClass; +#define TEST_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_OBJECT, TestObjectPrivate)) +typedef struct _TestObject TestObject; +typedef struct _TestObjectClass TestObjectClass; +typedef struct _TestObjectPrivate TestObjectPrivate; struct _TestObject { GObject parent_instance; @@ -140,6 +142,11 @@ struct _TestObjectClass TestIface *iface_object, gpointer tdata); }; +struct _TestObjectPrivate +{ + int dummy1; + gdouble dummy2; +}; static void test_object_class_init (TestObjectClass *class); static void test_object_init (TestObject *tobject); static gboolean test_signal_accumulator (GSignalInvocationHint *ihint, @@ -190,10 +197,18 @@ test_object_class_init (TestObjectClass *class) test_signal_accumulator, NULL, g_cclosure_marshal_STRING__OBJECT_POINTER, G_TYPE_STRING, 2, TEST_TYPE_IFACE, G_TYPE_POINTER); + + g_type_class_add_private (class, sizeof (TestObjectPrivate)); } static void test_object_init (TestObject *tobject) { + TestObjectPrivate *priv; + + priv = TEST_OBJECT_GET_PRIVATE (tobject); + + g_assert (priv); + g_assert ((gchar *)priv >= (gchar *)tobject + sizeof (TestObject)); } static gboolean test_signal_accumulator (GSignalInvocationHint *ihint, @@ -274,8 +289,16 @@ derived_object_test_iface_init (gpointer giface, #define DERIVED_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), DERIVED_TYPE_OBJECT)) #define DERIVED_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DERIVED_TYPE_OBJECT)) #define DERIVED_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DERIVED_TYPE_OBJECT, DerivedObjectClass)) -typedef struct _TestObject DerivedObject; -typedef struct _TestObjectClass DerivedObjectClass; +#define DERIVED_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DERIVED_TYPE_OBJECT, DerivedObjectPrivate)) +typedef struct _TestObject DerivedObject; +typedef struct _TestObjectClass DerivedObjectClass; +typedef struct _DerivedObjectPrivate DerivedObjectPrivate; +struct _DerivedObjectPrivate +{ + char dummy; +}; +static void derived_object_class_init (DerivedObjectClass *class); +static void derived_object_init (DerivedObject *dobject); GType derived_object_get_type (void) { @@ -288,12 +311,12 @@ derived_object_get_type (void) sizeof (DerivedObjectClass), NULL, /* base_init */ NULL, /* base_finalize */ - NULL, /* class_init */ + (GClassInitFunc) derived_object_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (DerivedObject), 5, /* n_preallocs */ - NULL, /* instance_init */ + (GInstanceInitFunc) derived_object_init, }; GInterfaceInfo iface_info = { derived_object_test_iface_init, NULL, GUINT_TO_POINTER (87) }; @@ -303,7 +326,28 @@ derived_object_get_type (void) return derived_object_type; } +static void +derived_object_class_init (DerivedObjectClass *class) +{ + g_type_class_add_private (class, sizeof (DerivedObjectPrivate)); +} +static void +derived_object_init (DerivedObject *dobject) +{ + TestObjectPrivate *test_priv; + DerivedObjectPrivate *derived_priv; + derived_priv = DERIVED_OBJECT_GET_PRIVATE (dobject); + + g_assert (derived_priv); + g_assert ((gchar *)derived_priv >= (gchar *)TEST_OBJECT_GET_PRIVATE (dobject) + sizeof (TestObjectPrivate)); + + test_priv = TEST_OBJECT_GET_PRIVATE (dobject); + + g_assert (test_priv); + g_assert ((gchar *)test_priv >= (gchar *)dobject + sizeof (TestObject)); + +} /* --- main --- */ int