[PATCH] Reordering interface base initialization
- From: Owen Taylor <otaylor redhat com>
- To: timj gtk org
- Cc: gtk-devel-list gnome org
- Subject: [PATCH] Reordering interface base initialization
- Date: Tue, 26 Aug 2003 20:25:16 -0400
Hi Tim -
Here's the first part of what we discussed for redoing the
interface property changes. What this patch does is split
out allocation and base initialization of interface vtables
from calling the interface init function, and order initialization
as:
Call base_init functions for class
Call base_init functions for interfaces
Call class_init function
Call interface_init functions
The point being, as you recall, we need to register the
interface properties we are going to override before we call
the class_init function, so that they can be found by
g_object_class_override_property().
The implementation here is relatively straightforward. To handle
reentrancy, I found it necessary to add an "initialization state"
enumeration field to the ClassData and IFaceEntry structures.
The cost of this is
4 bytes * sum_{classes} (class->n_ifaces)
So, on the order of maybe 500-600 bytes for a GTK+ program currently.
There were spare bits in ClassData where this information could
be put but none in IFaceEntry. If you wanted to get funky, you
could probably squeeze the the necessary information into the
2 spare alignment bits at the bottom of iface_entry->vtable, but
that would be icky. A better possibility would be to keep a
look-aside data structure holding information about what interfaces
have been initialized for each class we are currently initializing.
The way I have it here is fairly clean and simple, which may
be more important than a small amount of memory saving.
The initialization state field for ClassData may also be useful
for fixing the thread-safety-for-class-initialization problem:
http://bugzilla.gnome.org/show_bug.cgi?id=64764
Though I haven't really thought through the consequences.
Please give the patch a read-through; it would be nice to
get this out of the way before tacking class_init for interfaces,
which is going to overlap rather heavily.
Thanks,
Owen
Index: gtype.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gtype.c,v
retrieving revision 1.64
diff -u -p -r1.64 gtype.c
--- gtype.c 19 Aug 2003 03:25:46 -0000 1.64
+++ gtype.c 27 Aug 2003 00:19:23 -0000
@@ -155,6 +155,23 @@ static gboolean type_node_is_a_L (Ty
TypeNode *iface_node);
+/* --- enumeration --- */
+
+/* The InitState enumeration is used to track the progress of initializing
+ * both classes and interface vtables. Keeping the state of initialization
+ * is necessary to handle new interfaces being added while we are initializing
+ * the class or other interfaces.
+ */
+typedef enum
+{
+ UNINITIALIZED,
+ BASE_CLASS_INIT,
+ BASE_IFACE_INIT,
+ CLASS_INIT,
+ IFACE_INIT,
+ INITIALIZED
+} InitState;
+
/* --- structures --- */
struct _TypeNode
{
@@ -211,6 +228,7 @@ struct _IFaceEntry
{
GType iface_type;
GTypeInterface *vtable;
+ InitState init_state;
};
struct _CommonData
{
@@ -228,6 +246,7 @@ struct _ClassData
{
CommonData common;
guint16 class_size;
+ guint init_state : 4;
GBaseInitFunc class_init_base;
GBaseFinalizeFunc class_finalize_base;
GClassInitFunc class_init;
@@ -245,6 +264,7 @@ struct _InstanceData
GClassFinalizeFunc class_finalize;
gconstpointer class_data;
gpointer class;
+ InitState init_state;
guint16 instance_size;
guint16 private_size;
guint16 n_preallocs;
@@ -359,7 +379,10 @@ type_node_any_new_W (TypeNode
sizeof (CLASSED_NODE_IFACES_ENTRIES (pnode)[0]) *
CLASSED_NODE_N_IFACES (node));
for (j = 0; j < CLASSED_NODE_N_IFACES (node); j++)
- CLASSED_NODE_IFACES_ENTRIES (node)[j].vtable = NULL;
+ {
+ CLASSED_NODE_IFACES_ENTRIES (node)[j].vtable = NULL;
+ CLASSED_NODE_IFACES_ENTRIES (node)[j].init_state = UNINITIALIZED;
+ }
}
i = pnode->n_children++;
@@ -926,6 +949,7 @@ type_data_make_W (TypeNode
data->instance.class_finalize = info->class_finalize;
data->instance.class_data = info->class_data;
data->instance.class = NULL;
+ data->instance.init_state = UNINITIALIZED;
data->instance.instance_size = info->instance_size;
/* We'll set the final value for data->instance.private size
* after the parent class has been initialized
@@ -951,6 +975,7 @@ type_data_make_W (TypeNode
data->class.class_finalize = info->class_finalize;
data->class.class_data = info->class_data;
data->class.class = NULL;
+ data->class.init_state = UNINITIALIZED;
}
else if (NODE_IS_IFACE (node))
{
@@ -1095,6 +1120,7 @@ type_node_add_iface_entry_W (TypeNode *n
g_memmove (entries + i + 1, entries + i, sizeof (entries[0]) * (CLASSED_NODE_N_IFACES (node) - i - 1));
entries[i].iface_type = iface_type;
entries[i].vtable = NULL;
+ entries[i].init_state = UNINITIALIZED;
for (i = 0; i < node->n_children; i++)
type_node_add_iface_entry_W (lookup_type_node_I (node->children[i]), iface_type);
@@ -1555,19 +1581,28 @@ g_type_free_instance (GTypeInstance *ins
g_type_class_unref (class);
}
+/* This is called to allocate and do the first part of initializing
+ * the interface vtable; type_iface_vtable_init_Wm() does the remainder.
+ *
+ * A FALSE return indicates that we didn't find an init function for
+ * this type/iface pair, so the vtable from the parent type should
+ * be used.
+ */
static gboolean
-type_iface_vtable_init_Wm (TypeNode *iface,
- TypeNode *node)
+type_iface_vtable_base_init_Wm (TypeNode *iface,
+ TypeNode *node)
{
IFaceEntry *entry = type_lookup_iface_entry_L (node, iface);
IFaceHolder *iholder;
GTypeInterface *vtable = NULL;
TypeNode *pnode;
+
+ entry->init_state = IFACE_INIT;
/* type_iface_retrieve_holder_info_Wm() doesn't modify write lock for returning NULL */
iholder = type_iface_retrieve_holder_info_Wm (iface, NODE_TYPE (node), TRUE);
if (!iholder)
- return FALSE; /* we don't modify write lock upon FALSE */
+ return FALSE;
g_assert (iface->data && entry && entry->vtable == NULL && iholder && iholder->info);
@@ -1585,16 +1620,45 @@ type_iface_vtable_init_Wm (TypeNode *ifa
vtable->g_type = NODE_TYPE (iface);
vtable->g_instance_type = NODE_TYPE (node);
- if (iface->data->iface.vtable_init_base || iholder->info->interface_init)
+ if (iface->data->iface.vtable_init_base)
{
G_WRITE_UNLOCK (&type_rw_lock);
if (iface->data->iface.vtable_init_base)
iface->data->iface.vtable_init_base (vtable);
+ G_WRITE_LOCK (&type_rw_lock);
+ }
+ return TRUE; /* initialized the vtable */
+}
+
+/* Finishes what type_iface_vtable_base_init_Wm started by
+ * calling the interface init function.
+ */
+static void
+type_iface_vtable_init_Wm (TypeNode *iface,
+ TypeNode *node)
+{
+ IFaceEntry *entry = type_lookup_iface_entry_L (node, iface);
+ IFaceHolder *iholder;
+ GTypeInterface *vtable = NULL;
+
+ entry->init_state = INITIALIZED;
+
+ iholder = type_iface_peek_holder_L (iface, NODE_TYPE (node));
+ if (!iholder)
+ return;
+
+ /* iholder->info should have been filled in by type_iface_vtable_base_init_Wm() */
+ g_assert (iface->data && entry && iholder->info);
+
+ vtable = entry->vtable;
+
+ if (iholder->info->interface_init)
+ {
+ G_WRITE_UNLOCK (&type_rw_lock);
if (iholder->info->interface_init)
iholder->info->interface_init (vtable, iholder->info->interface_data);
G_WRITE_LOCK (&type_rw_lock);
}
- return TRUE; /* write lock modified */
}
static gboolean
@@ -1613,6 +1677,7 @@ type_iface_vtable_finalize_Wm (TypeNode
g_assert (entry && entry->vtable == vtable && iholder->info);
entry->vtable = NULL;
+ entry->init_state = UNINITIALIZED;
if (iholder->info->interface_finalize || iface->data->iface.vtable_finalize_base)
{
G_WRITE_UNLOCK (&type_rw_lock);
@@ -1643,10 +1708,12 @@ type_class_init_Wm (TypeNode *node,
g_assert (node->is_classed && node->data &&
node->data->class.class_size &&
- !node->data->class.class);
+ !node->data->class.class &&
+ node->data->class.init_state == UNINITIALIZED);
class = g_malloc0 (node->data->class.class_size);
node->data->class.class = class;
+ node->data->class.init_state = BASE_CLASS_INIT;
if (pclass)
{
@@ -1681,21 +1748,30 @@ type_class_init_Wm (TypeNode *node,
}
g_slist_free (init_slist);
- if (node->data->class.class_init)
- node->data->class.class_init (class, (gpointer) node->data->class.class_data);
-
G_WRITE_LOCK (&type_rw_lock);
+
+ node->data->class.init_state = BASE_IFACE_INIT;
- /* ok, we got the class done, now initialize all interfaces, either
+ /* Before we initialize the class, base initialize all interfaces, either
* from parent, or through our holder info
*/
pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));
- entry = CLASSED_NODE_IFACES_ENTRIES (node) + 0;
- while (entry)
+
+ i = 0;
+ while (i < CLASSED_NODE_N_IFACES (node))
{
- g_assert (entry->vtable == NULL);
+ entry = &CLASSED_NODE_IFACES_ENTRIES (node)[i];
+ while (i < CLASSED_NODE_N_IFACES (node) &&
+ entry->init_state != UNINITIALIZED)
+ {
+ entry++;
+ i++;
+ }
+
+ if (i == CLASSED_NODE_N_IFACES (node))
+ break;
- if (!type_iface_vtable_init_Wm (lookup_type_node_I (entry->iface_type), node))
+ if (!type_iface_vtable_base_init_Wm (lookup_type_node_I (entry->iface_type), node))
{
guint j;
@@ -1711,17 +1787,61 @@ type_class_init_Wm (TypeNode *node,
if (pentry->iface_type == entry->iface_type)
{
entry->vtable = pentry->vtable;
+ entry->init_state = INITIALIZED;
break;
}
}
g_assert (entry->vtable != NULL);
}
+
+ /* If the write lock was released, additional interface entries might
+ * have been inserted into CLASSED_NODE_IFACES_ENTRIES (node); they'll
+ * be base-initialized when inserted, so we don't have to worry that
+ * we might miss them. Uninitialized entries can only be moved higher
+ * when new ones are inserted.
+ */
+ i++;
+ }
+
+ node->data->class.init_state = CLASS_INIT;
+
+ G_WRITE_UNLOCK (&type_rw_lock);
+
+ if (node->data->class.class_init)
+ node->data->class.class_init (class, (gpointer) node->data->class.class_data);
+
+ G_WRITE_LOCK (&type_rw_lock);
+
+ node->data->class.init_state = IFACE_INIT;
+
+ /* finish initialize the interfaces through our holder info
+ */
+ i = 0;
+ while (TRUE)
+ {
+ entry = &CLASSED_NODE_IFACES_ENTRIES (node)[i];
+ while (i < CLASSED_NODE_N_IFACES (node) &&
+ entry->init_state == INITIALIZED)
+ {
+ entry++;
+ i++;
+ }
+
+ if (i == CLASSED_NODE_N_IFACES (node))
+ break;
+
+ g_assert (entry->init_state == IFACE_INIT);
+
+ type_iface_vtable_init_Wm (lookup_type_node_I (entry->iface_type), node);
- /* refetch entry, IFACES_ENTRIES might be modified */
- for (entry = NULL, i = 0; i < CLASSED_NODE_N_IFACES (node); i++)
- if (!CLASSED_NODE_IFACES_ENTRIES (node)[i].vtable)
- entry = CLASSED_NODE_IFACES_ENTRIES (node) + i;
+ /* As in the loop above, additional initialized entries might be inserted
+ * if the write lock is released, but that's harmless because the entries
+ * we need to initialize only move higher in the list.
+ */
+ i++;
}
+
+ node->data->class.init_state = INITIALIZED;
}
static void
@@ -2049,10 +2169,13 @@ g_type_add_interface_static (GType
if (check_interface_info_I (iface, NODE_TYPE (node), info))
{
type_add_interface_W (node, iface, info, NULL);
- /* if we have a class already, the interface vtable needs to
- * be initialized as well
+ /* If we have a class already, we may need to base initalize
+ * and/or initialize the new interface.
*/
- if (node->data && node->data->class.class)
+ if (node->data && node->data->class.init_state >= BASE_IFACE_INIT)
+ type_iface_vtable_base_init_Wm (iface, node);
+
+ if (node->data && node->data->class.init_state >= IFACE_INIT)
type_iface_vtable_init_Wm (iface, node);
}
}
@@ -2080,10 +2203,13 @@ g_type_add_interface_dynamic (GType
TypeNode *iface = lookup_type_node_I (interface_type);
type_add_interface_W (node, iface, NULL, plugin);
- /* if we have a class already, the interface vtable needs to
- * be initialized as well
+ /* If we have a class already, we may need to base initalize
+ * and/or initialize the new interface.
*/
- if (node->data && node->data->class.class)
+ if (node->data && node->data->class.init_state >= BASE_IFACE_INIT)
+ type_iface_vtable_base_init_Wm (iface, node);
+
+ if (node->data && node->data->class.init_state >= IFACE_INIT)
type_iface_vtable_init_Wm (iface, node);
}
G_WRITE_UNLOCK (&type_rw_lock);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]