Add type_data_ref_U() and use it in g_type_class_ref()
authorEdward Hervey <bilboed@bilboed.com>
Thu, 24 Sep 2009 10:42:49 +0000 (12:42 +0200)
committerAlexander Larsson <alexl@redhat.com>
Mon, 30 Nov 2009 19:52:33 +0000 (20:52 +0100)
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

index 9c7e2e9..412e9cc 100644 (file)
@@ -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;