From 35c376a8a6be94f6fd5c22164a5e0968f4d3e26e Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Thu, 24 Sep 2009 12:42:49 +0200 Subject: [PATCH] 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 file 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; -- 2.7.4