[glib] Add type_data_ref_U() and use it in g_type_class_ref()



commit 35c376a8a6be94f6fd5c22164a5e0968f4d3e26e
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 |   62 +++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 42 insertions(+), 20 deletions(-)
---
diff --git a/gobject/gtype.c b/gobject/gtype.c
index 9c7e2e9..412e9cc 100644
--- a/gobject/gtype.c
+++ b/gobject/gtype.c
@@ -1203,6 +1203,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,
@@ -2816,6 +2831,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);
@@ -2826,34 +2843,39 @@ 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 */
+
+  /* we need an initialized parent class for initializing derived classes */
+  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 */
-    {
-      /* we need an initialized parent class for initializing derived classes */
-      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);
-      if (pclass)
-        g_type_class_unref (pclass);
-    }
+    type_class_init_Wm (node, pclass);
+
+  G_WRITE_UNLOCK (&type_rw_lock);
+
+  if (pclass)
+    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]