[glib/wip/gcleanup: 66/106] gtype: Implement cleanup of GType



commit 8b110f591bfd41d90a607bc4866f3b62eb2a5a91
Author: Stef Walter <stefw gnome org>
Date:   Thu Nov 7 22:49:17 2013 +0100

    gtype: Implement cleanup of GType
    
    Partially from patches from Ole André Vadla Ravnås, and Dan Winship
    
    There are a lot of intertwined pointer references between
    types, their vtables, and other bits. Free the memory for them
    all together.
    
    Note that we still do all finalization (ie: callbacks) per type
    and module, in the relevant cleanup lists.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=711778

 gobject/gtype.c |  240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 gobject/gtype.h |    4 +-
 2 files changed, 242 insertions(+), 2 deletions(-)
---
diff --git a/gobject/gtype.c b/gobject/gtype.c
index 9ede33c..52d1529 100644
--- a/gobject/gtype.c
+++ b/gobject/gtype.c
@@ -202,6 +202,7 @@ static void                             type_iface_vtable_iface_init_Wm (TypeNod
                                                                          TypeNode               *node);
 static gboolean                                type_node_is_a_L                (TypeNode               *node,
                                                                         TypeNode               *iface_node);
+static void                             type_cleanup                    (void);
 
 
 /* --- enumeration --- */
@@ -2510,7 +2511,6 @@ g_type_remove_class_cache_func (gpointer            cache_data,
               cache_func, cache_data);
 }
 
-
 /**
  * g_type_add_interface_check: (skip)
  * @check_data: data to pass to @check_func
@@ -4430,6 +4430,92 @@ gobject_init_ctor (void)
   /* Signal system
    */
   _g_signal_init ();
+
+  G_CLEANUP_FUNC_IN_PHASE (type_cleanup, G_CLEANUP_PHASE_GRAVEYARD);
+}
+
+static void
+type_iface_vtable_finalize (TypeNode       *iface,
+                           TypeNode       *node,
+                           GTypeInterface *vtable)
+{
+  IFaceEntry *entry = type_lookup_iface_entry_L (node, iface);
+  IFaceHolder *iholder;
+
+  iholder = type_iface_retrieve_holder_info_Wm (iface, NODE_TYPE (node), FALSE);
+  if (!iholder)
+    return;
+
+  g_assert (entry && entry->vtable == vtable && iholder->info);
+
+  g_assert (entry->init_state == INITIALIZED);
+  entry->init_state = UNINITIALIZED;
+
+  if (iholder->info->interface_finalize)
+    iholder->info->interface_finalize (vtable, iholder->info->interface_data);
+  if (iface->data->iface.vtable_finalize_base)
+    iface->data->iface.vtable_finalize_base (vtable);
+}
+
+static void
+type_data_finalize_class_ifaces (TypeNode *node)
+{
+  IFaceEntries *entries;
+  guint i;
+
+  entries = CLASSED_NODE_IFACES_ENTRIES_LOCKED (node);
+  for (i = 0; entries != NULL && i < IFACE_ENTRIES_N_ENTRIES (entries); i++)
+    {
+      IFaceEntry *entry = &entries->entry[i];
+      if (entry->vtable)
+       {
+          g_assert (entry->init_state == INITIALIZED);
+
+          type_iface_vtable_finalize (lookup_type_node_I (entry->iface_type), node, entry->vtable);
+
+          entry->init_state = UNINITIALIZED;
+       }
+    }
+}
+
+static void
+type_data_finalize_class (TypeNode  *node,
+                         ClassData *cdata)
+{
+  GTypeClass *class = cdata->class;
+  TypeNode *bnode;
+
+  if (cdata->class_finalize)
+      cdata->class_finalize (class, (gpointer) cdata->class_data);
+
+  /* call all base class destruction functions in descending order
+   */
+  if (cdata->class_finalize_base)
+    cdata->class_finalize_base (class);
+  for (bnode = lookup_type_node_I (NODE_PARENT_TYPE (node)); bnode; bnode = lookup_type_node_I 
(NODE_PARENT_TYPE (bnode)))
+    if (bnode->data->class.class_finalize_base)
+      bnode->data->class.class_finalize_base (class);
+}
+
+static void
+type_data_finalize (TypeNode *node)
+{
+  TypeData *tdata;
+
+  tdata = node->data;
+  if (node->is_classed && tdata->class.class)
+    {
+      if (CLASSED_NODE_IFACES_ENTRIES_LOCKED (node) != NULL)
+       type_data_finalize_class_ifaces (node);
+      type_data_finalize_class (node, &tdata->class);
+    }
+  else if (NODE_IS_IFACE (node) && tdata->iface.dflt_vtable)
+    {
+      if (tdata->iface.dflt_finalize)
+        tdata->iface.dflt_finalize (tdata->iface.dflt_vtable, (gpointer) tdata->iface.dflt_data);
+      if (tdata->iface.vtable_finalize_base)
+        tdata->iface.vtable_finalize_base (tdata->iface.dflt_vtable);
+    }
 }
 
 /**
@@ -4847,3 +4933,155 @@ g_type_is_in_init (GType type)
 
   return node->data->class.init_state != INITIALIZED;
 }
+
+static void
+finalize_type (gpointer data)
+{
+  GType type = GPOINTER_TO_SIZE (data);
+  TypeNode *node;
+
+  node = lookup_type_node_I (type);
+  if (node->data && !node->plugin)
+    type_data_finalize (node);
+
+  if (node->global_gdata != NULL)
+    {
+      QData *qdatas = node->global_gdata->qdatas;
+      int i;
+
+      for (i = 0; i < node->global_gdata->n_qdatas; i++)
+        {
+          if (qdatas[i].destroy)
+            {
+              qdatas[i].destroy (qdatas[i].data);
+              qdatas[i].destroy = NULL;
+              qdatas[i].data = NULL;
+            }
+        }
+    }
+
+  if (NODE_IS_IFACE (node))
+    {
+      _g_signals_destroy (type);
+    }
+}
+
+static void
+type_cleanup (void)
+{
+  GHashTableIter iter;
+  gpointer value;
+  GHashTable *vtables;
+
+  static_n_class_cache_funcs = 0;
+  g_free (static_class_cache_funcs);
+  static_class_cache_funcs = NULL;
+
+  static_n_iface_check_funcs = 0;
+  g_free (static_iface_check_funcs);
+  static_iface_check_funcs = NULL;
+
+  g_hash_table_iter_init (&iter, static_type_nodes_ht);
+  vtables = g_hash_table_new (NULL, NULL);
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      GType gtype = (GType) GPOINTER_TO_SIZE (value);
+      TypeNode *node;
+
+      node = lookup_type_node_I (gtype);
+
+      g_free (node->children);
+
+      if (node->is_classed)
+        {
+          IFaceEntries *entries;
+
+          entries = CLASSED_NODE_IFACES_ENTRIES_LOCKED (node);
+          if (entries)
+            {
+              guint i;
+
+              for (i = 0; i != IFACE_ENTRIES_N_ENTRIES (entries); i++)
+                g_hash_table_insert (vtables, entries->entry[i].vtable, NULL);
+            }
+
+          _g_atomic_array_free (CLASSED_NODE_IFACES_ENTRIES (node));
+
+          if (node->data)
+            g_free (node->data->class.class);
+        }
+
+      if (NODE_IS_IFACE (node))
+        {
+          IFaceHolder *iholder, *next;
+
+          _g_atomic_array_free (&node->_prot.offsets);
+
+          iholder = iface_node_get_holders_L (node);
+          while (iholder)
+            {
+              next = iholder->next;
+
+              g_free (iholder->info);
+              g_free (iholder);
+
+              iholder = next;
+            }
+
+          if (node->data != NULL)
+            g_free (node->data->iface.dflt_vtable);
+
+          g_free (iface_node_get_dependants_array_L (node));
+        }
+
+      g_free (node->data);
+
+      if (node->global_gdata != NULL)
+        {
+          g_free (node->global_gdata->qdatas);
+          g_free (node->global_gdata);
+        }
+
+      g_free (node->prerequisites);
+
+      if (G_TYPE_IS_FUNDAMENTAL (gtype))
+        node = G_STRUCT_MEMBER_P (node, -SIZEOF_FUNDAMENTAL_INFO);
+
+      g_free (node);
+    }
+
+  g_hash_table_foreach (vtables, (GHFunc) g_free, NULL);
+  g_hash_table_unref (vtables);
+  g_hash_table_unref (static_type_nodes_ht);
+  static_type_nodes_ht = NULL;
+
+  _g_atomic_array_cleanup ();
+}
+
+void
+g_type_cleanup_push (GType type,
+                     GCleanupList *cleanup)
+{
+  gint phase;
+
+  /*
+   * For types in our own module, we want them finalized before
+   * the above infrastructure.
+   */
+  if (cleanup == G_CLEANUP_SCOPE)
+    phase = G_CLEANUP_PHASE_DEFAULT;
+  else
+    phase = G_CLEANUP_PHASE_LATE;
+
+  /*
+   * We want all the finalizers called first. They refer to each
+   * other so we can't free anything at this point.
+   */
+  g_cleanup_list_push (cleanup, phase, finalize_type,
+                       GSIZE_TO_POINTER (type), g_type_name (type));
+
+  /*
+   * And at the very end memory is freed. This is all done in one shot
+   * in the graveyard shift, due to intertwined pointers. See type_cleanup().
+   */
+}
diff --git a/gobject/gtype.h b/gobject/gtype.h
index ca6bc66..d498c11 100644
--- a/gobject/gtype.h
+++ b/gobject/gtype.h
@@ -732,7 +732,9 @@ gpointer              g_type_get_qdata               (GType            type,
 GLIB_AVAILABLE_IN_ALL
 void                 g_type_query                   (GType            type,
                                                      GTypeQuery      *query);
-
+GLIB_AVAILABLE_IN_2_40
+void                  g_type_cleanup_push            (GType            type,
+                                                      GCleanupList    *cleanup);
 
 /* --- type registration --- */
 /**


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