[glib/gobject-performance] Add type_data_ref_U() and use it in g_type_class_ref()



commit 4659a983ea1a2518c06b3f565512a12dddf84e89
Author: Edward Hervey <bilboed bilboed com>
Date:   Thu Sep 24 12:42:49 2009 +0200

    Add type_data_ref_U() and use it in g_type_class_ref()
    
    The function returns TRUE if the type was previously initialized and can
    be easily reused. It returns FALSE and does not take a reference if the
    type is not referenced yet.
    
    g_type_class_ref() uses this to avoid taking locks in the common path,
    which speeds up object creation a lot - in particular in multithreaded
    applications.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=585375

 gobject/gtype.c |   54 ++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 42 insertions(+), 12 deletions(-)
---
diff --git a/gobject/gtype.c b/gobject/gtype.c
index b5d44f0..c4840bc 100644
--- a/gobject/gtype.c
+++ b/gobject/gtype.c
@@ -1201,6 +1201,21 @@ type_data_ref_Wm (TypeNode *node)
     }
 }
 
+static inline gboolean
+type_data_ref_U (TypeNode *node)
+{
+  guint current;
+
+  do {
+    current = NODE_REFCOUNT (node);
+
+    if (current < 1)
+      return FALSE;
+  } while (!g_atomic_int_compare_and_exchange ((int *) &node->ref_count, current, current + 1));
+
+  return TRUE;
+}
+
 static gboolean
 iface_node_has_available_offset_L (TypeNode *iface_node,
 				   int offset,
@@ -2814,6 +2829,8 @@ g_type_class_ref (GType type)
 {
   TypeNode *node;
   GType ptype;
+  gboolean holds_ref;
+  GTypeClass *pclass;
 
   /* optimize for common code path */
   node = lookup_type_node_I (type);
@@ -2824,32 +2841,45 @@ g_type_class_ref (GType type)
       return NULL;
     }
 
-  G_WRITE_LOCK (&type_rw_lock);
-  type_data_ref_Wm (node);
-  if (g_atomic_int_get (&node->data->class.init_state) == INITIALIZED)
+  if (G_LIKELY (type_data_ref_U (node)))
     {
-      G_WRITE_UNLOCK (&type_rw_lock);
-      return node->data->class.class;
+      if (G_LIKELY (g_atomic_int_get (&node->data->class.init_state) == INITIALIZED))
+        return node->data->class.class;
+      holds_ref = TRUE;
     }
-  ptype = NODE_PARENT_TYPE (node);
-  G_WRITE_UNLOCK (&type_rw_lock);
-
-  g_static_rec_mutex_lock (&class_init_rec_mutex); /* required locking order: 1) class_init_rec_mutex, 2) type_rw_lock */
+  else
+    holds_ref = FALSE;
+  
   /* here, we either have node->data->class.class == NULL, or a recursive
    * call to g_type_class_ref() with a partly initialized class, or
    * node->data->class.init_state == INITIALIZED, because any
    * concurrently running initialization was guarded by class_init_rec_mutex.
    */
+  g_static_rec_mutex_lock (&class_init_rec_mutex); /* required locking order: 1) class_init_rec_mutex, 2) type_rw_lock */
+
+  /* acquire reference on parent class */
+  ptype = NODE_PARENT_TYPE (node);
+  pclass = ptype ? g_type_class_ref (ptype) : NULL;
+
+  G_WRITE_LOCK (&type_rw_lock);
+
+  if (!holds_ref)
+    type_data_ref_Wm (node);
+
   if (!node->data->class.class) /* class uninitialized */
     {
-      /* acquire reference on parent class */
-      GTypeClass *pclass = ptype ? g_type_class_ref (ptype) : NULL;
-      G_WRITE_LOCK (&type_rw_lock);
       if (node->data->class.class) /* class was initialized during parent class initialization? */
         INVALID_RECURSION ("g_type_plugin_*", node->plugin, NODE_NAME (node));
       type_class_init_Wm (node, pclass);
       G_WRITE_UNLOCK (&type_rw_lock);
     }
+  else
+    {
+      /* release ref to parent class, it wasn't needed */
+      G_WRITE_UNLOCK (&type_rw_lock);
+      g_type_class_unref (pclass);
+    }
+
   g_static_rec_mutex_unlock (&class_init_rec_mutex);
 
   return node->data->class.class;



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