Re: [PATCH] Reordering interface base initialization



On Tue, 2003-08-26 at 20:25, Owen Taylor wrote:

> 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.

I went ahead and added the lookaside. Also wrote a fairly comprehensive
test case that adds classes at various different points in the 
during class init.

Still need to improve the test case a bit more:

 - Needs to test adding interfaces in the instance class base_init
 - Need to test ordering as well as simply getting all the 
   interface_init and base_init functions called exactly once.

Regards,
						Owen


Index: testifaceinit.c
===================================================================
RCS file: testifaceinit.c
diff -N testifaceinit.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testifaceinit.c	27 Aug 2003 04:00:16 -0000
@@ -0,0 +1,312 @@
+/* GObject - GLib Type, Object, Parameter and Signal Library
+ * Copyright (C) 2001, 2003 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#undef	G_LOG_DOMAIN
+#define	G_LOG_DOMAIN "TestIfaceInit"
+#include	<glib-object.h>
+
+/* What this test tests is the ability to add interfaces dynamically; in
+ * particular adding interfaces to a class while that class is being
+ * initialized.
+ *
+ * The test defines 5 interfaces:
+ * 
+ * - TestIface1 is added in test_object_get_type1()
+ * - TestIface2 is added in test_iface1_base_init()
+ * - TestIface3 is added in test_object_class_init()
+ * - TestIface4 is added in test_object_test_iface1_init()
+ * - TestIface5 is added after the type is initialized
+ */
+
+#define DEFINE_IFACE(name, prefix, base_init)			\
+GType								\
+prefix ## _get_type (void)					\
+{								\
+  static GType iface_type = 0;					\
+								\
+  if (!iface_type)						\
+    {								\
+      static const GTypeInfo iface_info =			\
+      {								\
+	sizeof (name ## Class),					\
+	(GBaseInitFunc)	base_init,				\
+	(GBaseFinalizeFunc) NULL,				\
+      };							\
+								\
+      iface_type = g_type_register_static (G_TYPE_INTERFACE,	\
+					    # name,		\
+					    &iface_info, 0);	\
+    }								\
+  return iface_type;						\
+}
+
+/* All 5 interfaces actually share the same class structure, though
+ * we use separate typedefs
+ */
+typedef struct _TestIfaceClass TestIfaceClass;
+
+struct _TestIfaceClass
+{
+  GTypeInterface base_iface;
+  guint val;
+  guint base_val;
+};
+
+#define TEST_TYPE_IFACE1           (test_iface1_get_type ())
+#define TEST_IFACE1_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE1, TestIface1Class))
+typedef struct _TestIface1      TestIface1;
+typedef struct _TestIfaceClass  TestIface1Class;
+
+static void test_iface1_base_init (TestIface1Class *iface);
+
+static DEFINE_IFACE(TestIface1, test_iface1, test_iface1_base_init)
+
+#define TEST_TYPE_IFACE2           (test_iface2_get_type ())
+#define TEST_IFACE2_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE2, TestIface2Class))
+typedef struct _TestIface2      TestIface2;
+typedef struct _TestIfaceClass  TestIface2Class;
+
+static void  test_iface2_base_init (TestIface2Class *iface);
+
+static DEFINE_IFACE(TestIface2, test_iface2, test_iface2_base_init)
+
+#define TEST_TYPE_IFACE3           (test_iface3_get_type ())
+#define TEST_IFACE3_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE3, TestIface3Class))
+typedef struct _TestIface3      TestIface3;
+typedef struct _TestIfaceClass  TestIface3Class;
+
+static void  test_iface3_base_init (TestIface3Class *iface);
+
+static DEFINE_IFACE(TestIface3, test_iface3, test_iface3_base_init)
+
+#define TEST_TYPE_IFACE4           (test_iface4_get_type ())
+#define TEST_IFACE4_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE4, TestIface4Class))
+typedef struct _TestIface4      TestIface4;
+typedef struct _TestIfaceClass  TestIface4Class;
+
+static void  test_iface4_base_init (TestIface4Class *iface);
+
+static DEFINE_IFACE(TestIface4, test_iface4, test_iface4_base_init)
+
+#define TEST_TYPE_IFACE5           (test_iface5_get_type ())
+#define TEST_IFACE5_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE5, TestIface5Class))
+typedef struct _TestIface5      TestIface5;
+typedef struct _TestIfaceClass  TestIface5Class;
+
+static void  test_iface5_base_init (TestIface5Class *iface);
+
+static DEFINE_IFACE(TestIface5, test_iface5, test_iface5_base_init)
+
+#define TEST_TYPE_OBJECT          (test_object_get_type ())
+typedef struct _TestObject        TestObject;
+typedef struct _TestObjectClass   TestObjectClass;
+
+struct _TestObject
+{
+  GObject parent_instance;
+};
+struct _TestObjectClass
+{
+  GObjectClass parent_class;
+};
+
+#define TEST_CALLED_ONCE() G_STMT_START { \
+  static gboolean called = FALSE;         \
+  g_assert (!called);                     \
+  called = TRUE;                          \
+} G_STMT_END
+
+#define ADD_IFACE(n)  G_STMT_START {				\
+  static GInterfaceInfo iface_info = {				\
+    (GInterfaceInitFunc)test_object_test_iface##n##_init,	\
+    NULL, NULL };						\
+								\
+  g_type_add_interface_static (TEST_TYPE_OBJECT,		\
+			       test_iface##n##_get_type (),	\
+			       &iface_info);			\
+								\
+} G_STMT_END
+
+static void test_object_test_iface1_init (TestIface1Class *iface);
+static void test_object_test_iface2_init (TestIface2Class *iface);
+static void test_object_test_iface3_init (TestIface3Class *iface);
+static void test_object_test_iface4_init (TestIface4Class *iface);
+static void test_object_test_iface5_init (TestIface5Class *iface);
+
+static GType test_object_get_type (void);
+
+static void
+test_object_test_iface1_init (TestIface1Class *iface)
+{
+  TEST_CALLED_ONCE();
+  
+  iface->val = 0x10001;
+
+  ADD_IFACE(4);
+}
+
+static void
+test_object_test_iface2_init (TestIface2Class *iface)
+{
+  TEST_CALLED_ONCE();
+  
+  iface->val = 0x20002;
+}
+
+static void
+test_object_test_iface3_init (TestIface3Class *iface)
+{
+  TEST_CALLED_ONCE();
+
+  iface->val = 0x30003;
+}
+
+static void
+test_object_test_iface4_init (TestIface4Class *iface)
+{
+  TEST_CALLED_ONCE();
+
+  iface->val = 0x40004;
+}
+
+static void
+test_object_test_iface5_init (TestIface5Class *iface)
+{
+  TEST_CALLED_ONCE();
+
+  iface->val = 0x50005;
+}
+
+static void
+test_iface1_base_init (TestIface1Class *iface)
+{
+  TEST_CALLED_ONCE();
+
+  iface->base_val = 0x110011;
+  
+  ADD_IFACE(2);
+}
+
+static void
+test_iface2_base_init (TestIface2Class *iface)
+{
+  TEST_CALLED_ONCE();
+
+  iface->base_val = 0x220022;
+}
+
+static void
+test_iface3_base_init (TestIface3Class *iface)
+{
+  TEST_CALLED_ONCE();
+
+  iface->base_val = 0x330033;
+}
+
+static void
+test_iface4_base_init (TestIface4Class *iface)
+{
+  TEST_CALLED_ONCE();
+
+  iface->base_val = 0x440044;
+}
+
+static void
+test_iface5_base_init (TestIface5Class *iface)
+{
+  TEST_CALLED_ONCE();
+
+  iface->base_val = 0x550055;
+}
+
+static void
+test_object_class_init (TestObjectClass *class)
+{
+  ADD_IFACE(3);
+}
+
+static GType
+test_object_get_type (void)
+{
+  static GType test_object_type = 0;
+
+  if (!test_object_type)
+    {
+      static const GTypeInfo test_object_info =
+      {
+	sizeof (TestObjectClass),
+	NULL,           /* base_init */
+	NULL,           /* base_finalize */
+	(GClassInitFunc) test_object_class_init,
+	NULL,           /* class_finalize */
+	NULL,           /* class_data */
+	sizeof (TestObject),
+	5,              /* n_preallocs */
+	(GInstanceInitFunc) NULL,
+      };
+
+      test_object_type = g_type_register_static (G_TYPE_OBJECT, "TestObject", &test_object_info, 0);
+      ADD_IFACE(1);
+    }
+
+  return test_object_type;
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  TestObject *object;
+  TestObjectClass *object_class;
+  TestIfaceClass *iface;
+	
+  g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
+			  G_LOG_LEVEL_WARNING |
+			  G_LOG_LEVEL_CRITICAL);
+  g_type_init ();
+
+  /* We force the interfaces to be registered in a different order
+   * than we add them, so our logic doesn't always deal with interfaces
+   * added at the end.
+   */
+  (void)TEST_TYPE_IFACE3;
+  (void)TEST_TYPE_IFACE5;
+  (void)TEST_TYPE_IFACE4;
+  (void)TEST_TYPE_IFACE2;
+  (void)TEST_TYPE_IFACE1;
+
+  object_class = g_type_class_ref (TEST_TYPE_OBJECT);
+
+  ADD_IFACE(5);
+
+  object = g_object_new (TEST_TYPE_OBJECT, NULL);
+
+  iface = TEST_IFACE1_GET_CLASS (object);
+  g_assert (iface && iface->val == 0x10001 && iface->base_val == 0x110011);
+  iface = TEST_IFACE2_GET_CLASS (object);
+  g_assert (iface && iface->val == 0x20002 && iface->base_val == 0x220022);
+  iface = TEST_IFACE3_GET_CLASS (object);
+  g_assert (iface && iface->val == 0x30003 && iface->base_val == 0x330033);
+  iface = TEST_IFACE4_GET_CLASS (object);
+  g_assert (iface && iface->val == 0x40004 && iface->base_val == 0x440044);
+  iface = TEST_IFACE5_GET_CLASS (object);
+  g_assert (iface && iface->val == 0x50005 && iface->base_val == 0x550055);
+
+  return 0;
+}
Index: gtype.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gtype.c,v
retrieving revision 1.64
diff -u -p -u -r1.64 gtype.c
--- gtype.c	19 Aug 2003 03:25:46 -0000	1.64
+++ gtype.c	27 Aug 2003 04:00:17 -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;
@@ -239,6 +258,7 @@ struct _InstanceData
 {
   CommonData         common;
   guint16            class_size;
+  guint              init_state : 4;
   GBaseInitFunc      class_init_base;
   GBaseFinalizeFunc  class_finalize_base;
   GClassInitFunc     class_init;
@@ -258,6 +278,10 @@ union _TypeData
   ClassData          class;
   InstanceData       instance;
 };
+struct _ClassInitInfo
+{
+};
+
 typedef struct {
   gpointer            cache_data;
   GTypeClassCacheFunc cache_func;
@@ -359,7 +383,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 +953,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 +979,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 +1124,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 +1585,27 @@ 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_iface_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. Note that the write lock is not modified upon a FALSE
+ * return.
+ */
 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;
-  
+
   /* 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 +1623,43 @@ 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_iface_init_Wm (TypeNode *iface,
+				 TypeNode *node)
+{
+  IFaceEntry *entry = type_lookup_iface_entry_L (node, iface);
+  IFaceHolder *iholder;
+  GTypeInterface *vtable = NULL;
+  
+  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
@@ -1631,6 +1696,18 @@ type_iface_vtable_finalize_Wm (TypeNode 
   return TRUE;	/* write lock modified */
 }
 
+static GSList *initializing_classes = NULL;
+
+typedef struct {
+  TypeNode *init_node;
+  guchar *iface_states;
+} ClassInitInfo;
+
+/* Maximum number of interfaces a class can have before we store the
+ * temporary initialization array on the heap
+ */
+#define MAX_STACK_IFACE_STATES 32
+
 static void
 type_class_init_Wm (TypeNode   *node,
 		    GTypeClass *pclass)
@@ -1639,14 +1716,26 @@ type_class_init_Wm (TypeNode   *node,
   GTypeClass *class;
   IFaceEntry *entry;
   TypeNode *bnode, *pnode;
+  ClassInitInfo init_info;
+  guchar stack_iface_states[MAX_STACK_IFACE_STATES];
   guint i;
   
   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);
+
+  init_info.init_node = node;
+  if (CLASSED_NODE_N_IFACES (node) > MAX_STACK_IFACE_STATES)
+    init_info.iface_states = g_malloc (CLASSED_NODE_N_IFACES (node));
+  else
+    init_info.iface_states = stack_iface_states;
 
+  initializing_classes = g_slist_prepend (initializing_classes, &init_info);
+  
   class = g_malloc0 (node->data->class.class_size);
   node->data->class.class = class;
+  node->data->class.init_state = BASE_CLASS_INIT;
   
   if (pclass)
     {
@@ -1681,26 +1770,38 @@ 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)
+
+  memset (init_info.iface_states, 0, CLASSED_NODE_N_IFACES (node));
+  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) &&
+	     init_info.iface_states[i] == IFACE_INIT)
+	{
+	  entry++;
+	  i++;
+	}
+
+      if (i == CLASSED_NODE_N_IFACES (node))
+	break;
+
+      init_info.iface_states[i] = IFACE_INIT;
       
-      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;
 	  
-	  /* type_iface_vtable_init_Wm() doesn't modify write lock upon FALSE,
-	   * need to get this interface from parent
+	  /* need to get this interface from parent, type_iface_vtable_base_init_Wm()
+	   * doesn't modify write lock upon FALSE, so entry is still valid; 
 	   */
 	  g_assert (pnode != NULL);
 	  
@@ -1716,11 +1817,132 @@ type_class_init_Wm (TypeNode   *node,
 	    }
 	  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) &&
+	     init_info.iface_states[i] == INITIALIZED)
+	{
+	  entry++;
+	  i++;
+	}
+
+      if (i == CLASSED_NODE_N_IFACES (node))
+	break;
+
+      init_info.iface_states[i] = INITIALIZED;
+      
+      type_iface_vtable_iface_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;
+
+  if (init_info.iface_states != stack_iface_states)
+    g_free (init_info.iface_states);
+
+  initializing_classes = g_slist_remove (initializing_classes, &init_info);
+}
+
+/* This function is called when we add a new interface to an existing type;
+ * depending on whether the type already has a class or not, we call
+ * base-init and interface-init functions. If we are called *while* the
+ * class is being initialized, then we need to update an array saying which
+ * interfaces for the class have been initialized.
+ */
+static void
+type_iface_vtable_init_Wm (TypeNode *node,
+			   TypeNode *iface)
+{
+  InitState class_state =
+    node->data ? node->data->class.init_state : UNINITIALIZED;
+  InitState new_state = UNINITIALIZED;
+  
+  if (class_state >= BASE_IFACE_INIT)
+    {
+      type_iface_vtable_base_init_Wm (iface, node);
+      new_state = IFACE_INIT;
+    }
+
+  if (class_state >= IFACE_INIT)
+    {
+      type_iface_vtable_iface_init_Wm (iface, node);
+      new_state = INITIALIZED;
+    }
+  
+  if (class_state != UNINITIALIZED && class_state != INITIALIZED)
+    {
+      /* The interface was added while we were initializing the class
+       */
+      GSList *list;
+      int i;
+
+      for (i = 0; i < CLASSED_NODE_N_IFACES (node); i++)
+	if (CLASSED_NODE_IFACES_ENTRIES (node)[i].iface_type == NODE_TYPE (iface))
+	  break;
+
+      g_assert (i != CLASSED_NODE_N_IFACES (node)); /* We should have found the iface */
+
+      for (list = initializing_classes; list; list = list->next)
+	{
+	  ClassInitInfo *init_info = list->data;
+	  if (init_info->init_node == node)
+	    {
+	      int n_ifaces = CLASSED_NODE_N_IFACES (node);
+	      int old_n_ifaces = n_ifaces - 1;
+	      
+	      /* Reallocate the array if necessary; we can't use realloc
+	       * since the old array may have been on the stack
+	       */
+	      if (n_ifaces > MAX_STACK_IFACE_STATES)
+		{
+		  guchar *new_iface_states = g_malloc (n_ifaces);
+		  memcpy (new_iface_states, init_info->iface_states, old_n_ifaces);
+		  if (old_n_ifaces > MAX_STACK_IFACE_STATES)
+		    g_free (init_info->iface_states);
+		  init_info->iface_states = new_iface_states;
+		}
+
+	      /* Make room for the new values */
+	      g_memmove (init_info->iface_states + i + 1,
+			 init_info->iface_states + i,
+			 old_n_ifaces - i);
+	      init_info->iface_states[i] = new_state;
+
+	      break;
+	    }
+	}
+
+      g_assert (list);		/* We should have found the ClassInitInfo */
     }
 }
 
@@ -2049,11 +2271,10 @@ 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)
-	    type_iface_vtable_init_Wm (iface, node);
+	  type_iface_vtable_init_Wm (node, iface);
 	}
     }
   G_WRITE_UNLOCK (&type_rw_lock);
@@ -2080,11 +2301,10 @@ 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)
-	type_iface_vtable_init_Wm (iface, node);
+      type_iface_vtable_init_Wm (node, iface);
     }
   G_WRITE_UNLOCK (&type_rw_lock);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]