mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-13 07:56:17 +01:00
added recursive mutex to protect class initialization, default interface
2008-02-05 18:52:07 Tim Janik <timj@imendio.com> * gtype.c: added recursive mutex to protect class initialization, default interface initialization and per-class interface construction. a lock to this recursive mutex is held during user callback invocations such as initializers or finalizers, effectively allowing only one thread to run class/interface initializers/finalizers at a time. also made misc fixups. this fixes: Bug 64764 - Class initialization isn't thread safe. svn path=/trunk/; revision=6454
This commit is contained in:
parent
dfa5b08805
commit
0dd27821f2
@ -1,3 +1,13 @@
|
|||||||
|
2008-02-05 18:52:07 Tim Janik <timj@imendio.com>
|
||||||
|
|
||||||
|
* gtype.c: added recursive mutex to protect class initialization,
|
||||||
|
default interface initialization and per-class interface construction.
|
||||||
|
a lock to this recursive mutex is held during user callback invocations
|
||||||
|
such as initializers or finalizers, effectively allowing only one thread
|
||||||
|
to run class/interface initializers/finalizers at a time.
|
||||||
|
also made misc fixups. this fixes:
|
||||||
|
Bug 64764 - Class initialization isn't thread safe.
|
||||||
|
|
||||||
2008-02-05 18:41:22 Tim Janik <timj@imendio.com>
|
2008-02-05 18:41:22 Tim Janik <timj@imendio.com>
|
||||||
|
|
||||||
* Makefile.am: integrate tests/.
|
* Makefile.am: integrate tests/.
|
||||||
|
123
gobject/gtype.c
123
gobject/gtype.c
@ -59,9 +59,10 @@
|
|||||||
* - _Wm: [Write-locked invocation, mutatable]
|
* - _Wm: [Write-locked invocation, mutatable]
|
||||||
* like _W, but the write lock might be released and reacquired
|
* like _W, but the write lock might be released and reacquired
|
||||||
* during invocation, watch your pointers
|
* during invocation, watch your pointers
|
||||||
|
* - _WmREC: [Write-locked invocation, mutatable, recursive]
|
||||||
|
* like _Wm, but also acquires recursive mutex class_init_rec_mutex
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static GStaticRWLock type_rw_lock = G_STATIC_RW_LOCK_INIT;
|
|
||||||
#ifdef LOCK_DEBUG
|
#ifdef LOCK_DEBUG
|
||||||
#define G_READ_LOCK(rw_lock) do { g_printerr (G_STRLOC ": readL++\n"); g_static_rw_lock_reader_lock (rw_lock); } while (0)
|
#define G_READ_LOCK(rw_lock) do { g_printerr (G_STRLOC ": readL++\n"); g_static_rw_lock_reader_lock (rw_lock); } while (0)
|
||||||
#define G_READ_UNLOCK(rw_lock) do { g_printerr (G_STRLOC ": readL--\n"); g_static_rw_lock_reader_unlock (rw_lock); } while (0)
|
#define G_READ_UNLOCK(rw_lock) do { g_printerr (G_STRLOC ": readL--\n"); g_static_rw_lock_reader_unlock (rw_lock); } while (0)
|
||||||
@ -141,7 +142,7 @@ static void type_data_make_W (TypeNode *node,
|
|||||||
const GTypeInfo *info,
|
const GTypeInfo *info,
|
||||||
const GTypeValueTable *value_table);
|
const GTypeValueTable *value_table);
|
||||||
static inline void type_data_ref_Wm (TypeNode *node);
|
static inline void type_data_ref_Wm (TypeNode *node);
|
||||||
static inline void type_data_unref_Wm (TypeNode *node,
|
static inline void type_data_unref_WmREC (TypeNode *node,
|
||||||
gboolean uncached);
|
gboolean uncached);
|
||||||
static void type_data_last_unref_Wm (GType type,
|
static void type_data_last_unref_Wm (GType type,
|
||||||
gboolean uncached);
|
gboolean uncached);
|
||||||
@ -297,6 +298,8 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
/* --- variables --- */
|
/* --- variables --- */
|
||||||
|
static GStaticRWLock type_rw_lock = G_STATIC_RW_LOCK_INIT;
|
||||||
|
static GStaticRecMutex class_init_rec_mutex = G_STATIC_REC_MUTEX_INIT;
|
||||||
static guint static_n_class_cache_funcs = 0;
|
static guint static_n_class_cache_funcs = 0;
|
||||||
static ClassCacheFunc *static_class_cache_funcs = NULL;
|
static ClassCacheFunc *static_class_cache_funcs = NULL;
|
||||||
static guint static_n_iface_check_funcs = 0;
|
static guint static_n_iface_check_funcs = 0;
|
||||||
@ -1086,23 +1089,26 @@ type_data_ref_Wm (TypeNode *node)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
type_data_unref_Wm (TypeNode *node,
|
type_data_unref_WmREC (TypeNode *node,
|
||||||
gboolean uncached)
|
gboolean uncached)
|
||||||
{
|
{
|
||||||
g_assert (node->data && node->data->common.ref_count);
|
g_assert (node->data && node->data->common.ref_count);
|
||||||
|
|
||||||
if (node->data->common.ref_count > 1)
|
if (node->data->common.ref_count > 1)
|
||||||
node->data->common.ref_count -= 1;
|
node->data->common.ref_count -= 1;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
GType node_type = NODE_TYPE (node);
|
||||||
if (!node->plugin)
|
if (!node->plugin)
|
||||||
{
|
{
|
||||||
g_warning ("static type `%s' unreferenced too often",
|
g_warning ("static type `%s' unreferenced too often",
|
||||||
NODE_NAME (node));
|
NODE_NAME (node));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
G_WRITE_UNLOCK (&type_rw_lock);
|
||||||
type_data_last_unref_Wm (NODE_TYPE (node), uncached);
|
g_static_rec_mutex_lock (&class_init_rec_mutex); /* required locking order: 1) class_init_rec_mutex, 2) type_rw_lock */
|
||||||
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
|
type_data_last_unref_Wm (node_type, uncached);
|
||||||
|
g_static_rec_mutex_unlock (&class_init_rec_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1437,7 +1443,7 @@ type_iface_blow_holder_info_Wm (TypeNode *iface,
|
|||||||
g_type_plugin_unuse (iholder->plugin);
|
g_type_plugin_unuse (iholder->plugin);
|
||||||
G_WRITE_LOCK (&type_rw_lock);
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
|
|
||||||
type_data_unref_Wm (iface, FALSE);
|
type_data_unref_WmREC (iface, FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2057,7 +2063,7 @@ type_data_last_unref_Wm (GType type,
|
|||||||
g_type_plugin_unuse (node->plugin);
|
g_type_plugin_unuse (node->plugin);
|
||||||
G_WRITE_LOCK (&type_rw_lock);
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
if (ptype)
|
if (ptype)
|
||||||
type_data_unref_Wm (lookup_type_node_I (ptype), FALSE);
|
type_data_unref_WmREC (lookup_type_node_I (ptype), FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2173,7 +2179,7 @@ g_type_register_fundamental (GType type_id,
|
|||||||
if ((type_id & TYPE_ID_MASK) ||
|
if ((type_id & TYPE_ID_MASK) ||
|
||||||
type_id > G_TYPE_FUNDAMENTAL_MAX)
|
type_id > G_TYPE_FUNDAMENTAL_MAX)
|
||||||
{
|
{
|
||||||
g_warning ("attempt to register fundamental type `%s' with invalid type id (%lu)",
|
g_warning ("attempt to register fundamental type `%s' with invalid type id (%zu)",
|
||||||
type_name,
|
type_name,
|
||||||
type_id);
|
type_id);
|
||||||
return 0;
|
return 0;
|
||||||
@ -2307,17 +2313,22 @@ g_type_add_interface_static (GType instance_type,
|
|||||||
/* G_TYPE_IS_INSTANTIATABLE() is an external call: _U */
|
/* G_TYPE_IS_INSTANTIATABLE() is an external call: _U */
|
||||||
g_return_if_fail (G_TYPE_IS_INSTANTIATABLE (instance_type));
|
g_return_if_fail (G_TYPE_IS_INSTANTIATABLE (instance_type));
|
||||||
g_return_if_fail (g_type_parent (interface_type) == G_TYPE_INTERFACE);
|
g_return_if_fail (g_type_parent (interface_type) == G_TYPE_INTERFACE);
|
||||||
|
|
||||||
|
/* we only need to lock class_init_rec_mutex if instance_type already has its
|
||||||
|
* class initialized, however this function is rarely enough called to take
|
||||||
|
* the simple route and always acquire 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 */
|
||||||
G_WRITE_LOCK (&type_rw_lock);
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
if (check_add_interface_L (instance_type, interface_type))
|
if (check_add_interface_L (instance_type, interface_type))
|
||||||
{
|
{
|
||||||
TypeNode *node = lookup_type_node_I (instance_type);
|
TypeNode *node = lookup_type_node_I (instance_type);
|
||||||
TypeNode *iface = lookup_type_node_I (interface_type);
|
TypeNode *iface = lookup_type_node_I (interface_type);
|
||||||
|
|
||||||
if (check_interface_info_I (iface, NODE_TYPE (node), info))
|
if (check_interface_info_I (iface, NODE_TYPE (node), info))
|
||||||
type_add_interface_Wm (node, iface, info, NULL);
|
type_add_interface_Wm (node, iface, info, NULL);
|
||||||
}
|
}
|
||||||
G_WRITE_UNLOCK (&type_rw_lock);
|
G_WRITE_UNLOCK (&type_rw_lock);
|
||||||
|
g_static_rec_mutex_unlock (&class_init_rec_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -2326,23 +2337,24 @@ g_type_add_interface_dynamic (GType instance_type,
|
|||||||
GTypePlugin *plugin)
|
GTypePlugin *plugin)
|
||||||
{
|
{
|
||||||
TypeNode *node;
|
TypeNode *node;
|
||||||
|
|
||||||
/* G_TYPE_IS_INSTANTIATABLE() is an external call: _U */
|
/* G_TYPE_IS_INSTANTIATABLE() is an external call: _U */
|
||||||
g_return_if_fail (G_TYPE_IS_INSTANTIATABLE (instance_type));
|
g_return_if_fail (G_TYPE_IS_INSTANTIATABLE (instance_type));
|
||||||
g_return_if_fail (g_type_parent (interface_type) == G_TYPE_INTERFACE);
|
g_return_if_fail (g_type_parent (interface_type) == G_TYPE_INTERFACE);
|
||||||
|
|
||||||
node = lookup_type_node_I (instance_type);
|
node = lookup_type_node_I (instance_type);
|
||||||
if (!check_plugin_U (plugin, FALSE, TRUE, NODE_NAME (node)))
|
if (!check_plugin_U (plugin, FALSE, TRUE, NODE_NAME (node)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* see comment in g_type_add_interface_static() about 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 */
|
||||||
G_WRITE_LOCK (&type_rw_lock);
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
if (check_add_interface_L (instance_type, interface_type))
|
if (check_add_interface_L (instance_type, interface_type))
|
||||||
{
|
{
|
||||||
TypeNode *iface = lookup_type_node_I (interface_type);
|
TypeNode *iface = lookup_type_node_I (interface_type);
|
||||||
|
|
||||||
type_add_interface_Wm (node, iface, NULL, plugin);
|
type_add_interface_Wm (node, iface, NULL, plugin);
|
||||||
}
|
}
|
||||||
G_WRITE_UNLOCK (&type_rw_lock);
|
G_WRITE_UNLOCK (&type_rw_lock);
|
||||||
|
g_static_rec_mutex_unlock (&class_init_rec_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2373,27 +2385,33 @@ g_type_class_ref (GType type)
|
|||||||
type_descriptive_name_I (type));
|
type_descriptive_name_I (type));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
type_data_ref_Wm (node);
|
type_data_ref_Wm (node);
|
||||||
|
|
||||||
if (!node->data->class.class)
|
if (!node->data->class.class) /* class uninitialized */
|
||||||
{
|
{
|
||||||
GType ptype = NODE_PARENT_TYPE (node);
|
GType ptype = NODE_PARENT_TYPE (node);
|
||||||
GTypeClass *pclass = NULL;
|
GTypeClass *pclass = NULL;
|
||||||
|
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 */
|
||||||
if (ptype)
|
if (ptype)
|
||||||
{
|
{
|
||||||
G_WRITE_UNLOCK (&type_rw_lock);
|
pclass = g_type_class_ref (ptype);
|
||||||
pclass = g_type_class_ref (ptype);
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
if (node->data->class.class)
|
node = lookup_type_node_I (type);
|
||||||
|
if (node->data->class.class)
|
||||||
INVALID_RECURSION ("g_type_plugin_*", node->plugin, NODE_NAME (node));
|
INVALID_RECURSION ("g_type_plugin_*", node->plugin, NODE_NAME (node));
|
||||||
G_WRITE_LOCK (&type_rw_lock);
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
type_class_init_Wm (node, pclass);
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
|
node = lookup_type_node_I (type);
|
||||||
|
}
|
||||||
|
if (!node->data->class.class) /* class could have been initialized meanwhile */
|
||||||
|
type_class_init_Wm (node, pclass);
|
||||||
|
G_WRITE_UNLOCK (&type_rw_lock);
|
||||||
|
g_static_rec_mutex_unlock (&class_init_rec_mutex);
|
||||||
}
|
}
|
||||||
G_WRITE_UNLOCK (&type_rw_lock);
|
|
||||||
|
|
||||||
return node->data->class.class;
|
return node->data->class.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2409,7 +2427,7 @@ g_type_class_unref (gpointer g_class)
|
|||||||
G_WRITE_LOCK (&type_rw_lock);
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
if (node && node->is_classed && node->data &&
|
if (node && node->is_classed && node->data &&
|
||||||
node->data->class.class == class && node->data->common.ref_count > 0)
|
node->data->class.class == class && node->data->common.ref_count > 0)
|
||||||
type_data_unref_Wm (node, FALSE);
|
type_data_unref_WmREC (node, FALSE);
|
||||||
else
|
else
|
||||||
g_warning ("cannot unreference class of invalid (unclassed) type `%s'",
|
g_warning ("cannot unreference class of invalid (unclassed) type `%s'",
|
||||||
type_descriptive_name_I (class->g_type));
|
type_descriptive_name_I (class->g_type));
|
||||||
@ -2428,7 +2446,7 @@ g_type_class_unref_uncached (gpointer g_class)
|
|||||||
node = lookup_type_node_I (class->g_type);
|
node = lookup_type_node_I (class->g_type);
|
||||||
if (node && node->is_classed && node->data &&
|
if (node && node->is_classed && node->data &&
|
||||||
node->data->class.class == class && node->data->common.ref_count > 0)
|
node->data->class.class == class && node->data->common.ref_count > 0)
|
||||||
type_data_unref_Wm (node, TRUE);
|
type_data_unref_WmREC (node, TRUE);
|
||||||
else
|
else
|
||||||
g_warning ("cannot unreference class of invalid (unclassed) type `%s'",
|
g_warning ("cannot unreference class of invalid (unclassed) type `%s'",
|
||||||
type_descriptive_name_I (class->g_type));
|
type_descriptive_name_I (class->g_type));
|
||||||
@ -2562,9 +2580,10 @@ gpointer
|
|||||||
g_type_default_interface_ref (GType g_type)
|
g_type_default_interface_ref (GType g_type)
|
||||||
{
|
{
|
||||||
TypeNode *node;
|
TypeNode *node;
|
||||||
|
gpointer dflt_vtable;
|
||||||
|
|
||||||
G_WRITE_LOCK (&type_rw_lock);
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
|
|
||||||
node = lookup_type_node_I (g_type);
|
node = lookup_type_node_I (g_type);
|
||||||
if (!node || !NODE_IS_IFACE (node) ||
|
if (!node || !NODE_IS_IFACE (node) ||
|
||||||
(node->data && node->data->common.ref_count < 1))
|
(node->data && node->data->common.ref_count < 1))
|
||||||
@ -2574,14 +2593,24 @@ g_type_default_interface_ref (GType g_type)
|
|||||||
type_descriptive_name_I (g_type));
|
type_descriptive_name_I (g_type));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
type_data_ref_Wm (node);
|
|
||||||
|
|
||||||
type_iface_ensure_dflt_vtable_Wm (node);
|
if (!node->data || !node->data->iface.dflt_vtable)
|
||||||
|
{
|
||||||
|
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 */
|
||||||
|
G_WRITE_LOCK (&type_rw_lock);
|
||||||
|
node = lookup_type_node_I (g_type);
|
||||||
|
type_data_ref_Wm (node);
|
||||||
|
type_iface_ensure_dflt_vtable_Wm (node);
|
||||||
|
g_static_rec_mutex_unlock (&class_init_rec_mutex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
type_data_ref_Wm (node); /* ref_count >= 1 already */
|
||||||
|
|
||||||
|
dflt_vtable = node->data->iface.dflt_vtable;
|
||||||
G_WRITE_UNLOCK (&type_rw_lock);
|
G_WRITE_UNLOCK (&type_rw_lock);
|
||||||
|
|
||||||
return node->data->iface.dflt_vtable;
|
return dflt_vtable;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpointer
|
gpointer
|
||||||
@ -2614,7 +2643,7 @@ g_type_default_interface_unref (gpointer g_iface)
|
|||||||
if (node && NODE_IS_IFACE (node) &&
|
if (node && NODE_IS_IFACE (node) &&
|
||||||
node->data->iface.dflt_vtable == g_iface &&
|
node->data->iface.dflt_vtable == g_iface &&
|
||||||
node->data->common.ref_count > 0)
|
node->data->common.ref_count > 0)
|
||||||
type_data_unref_Wm (node, FALSE);
|
type_data_unref_WmREC (node, FALSE);
|
||||||
else
|
else
|
||||||
g_warning ("cannot unreference invalid interface default vtable for '%s'",
|
g_warning ("cannot unreference invalid interface default vtable for '%s'",
|
||||||
type_descriptive_name_I (vtable->g_type));
|
type_descriptive_name_I (vtable->g_type));
|
||||||
@ -3336,7 +3365,7 @@ g_type_value_table_peek (GType type)
|
|||||||
return vtable;
|
return vtable;
|
||||||
|
|
||||||
if (!node)
|
if (!node)
|
||||||
g_warning (G_STRLOC ": type id `%lu' is invalid", type);
|
g_warning (G_STRLOC ": type id `%zu' is invalid", type);
|
||||||
if (!has_refed_data)
|
if (!has_refed_data)
|
||||||
g_warning ("can't peek value table for type `%s' which is not currently referenced",
|
g_warning ("can't peek value table for type `%s' which is not currently referenced",
|
||||||
type_descriptive_name_I (type));
|
type_descriptive_name_I (type));
|
||||||
@ -3371,7 +3400,7 @@ g_type_init_with_debug_flags (GTypeDebugFlags debug_flags)
|
|||||||
const gchar *env_string;
|
const gchar *env_string;
|
||||||
GTypeInfo info;
|
GTypeInfo info;
|
||||||
TypeNode *node;
|
TypeNode *node;
|
||||||
GType type;
|
volatile GType votype;
|
||||||
|
|
||||||
G_LOCK (type_init_lock);
|
G_LOCK (type_init_lock);
|
||||||
|
|
||||||
@ -3415,16 +3444,16 @@ g_type_init_with_debug_flags (GTypeDebugFlags debug_flags)
|
|||||||
/* void type G_TYPE_NONE
|
/* void type G_TYPE_NONE
|
||||||
*/
|
*/
|
||||||
node = type_node_fundamental_new_W (G_TYPE_NONE, g_intern_static_string ("void"), 0);
|
node = type_node_fundamental_new_W (G_TYPE_NONE, g_intern_static_string ("void"), 0);
|
||||||
type = NODE_TYPE (node);
|
votype = NODE_TYPE (node);
|
||||||
g_assert (type == G_TYPE_NONE);
|
g_assert (votype == G_TYPE_NONE);
|
||||||
|
|
||||||
/* interface fundamental type G_TYPE_INTERFACE (!classed)
|
/* interface fundamental type G_TYPE_INTERFACE (!classed)
|
||||||
*/
|
*/
|
||||||
memset (&info, 0, sizeof (info));
|
memset (&info, 0, sizeof (info));
|
||||||
node = type_node_fundamental_new_W (G_TYPE_INTERFACE, g_intern_static_string ("GInterface"), G_TYPE_FLAG_DERIVABLE);
|
node = type_node_fundamental_new_W (G_TYPE_INTERFACE, g_intern_static_string ("GInterface"), G_TYPE_FLAG_DERIVABLE);
|
||||||
type = NODE_TYPE (node);
|
votype = NODE_TYPE (node);
|
||||||
type_data_make_W (node, &info, NULL);
|
type_data_make_W (node, &info, NULL);
|
||||||
g_assert (type == G_TYPE_INTERFACE);
|
g_assert (votype == G_TYPE_INTERFACE);
|
||||||
|
|
||||||
G_WRITE_UNLOCK (&type_rw_lock);
|
G_WRITE_UNLOCK (&type_rw_lock);
|
||||||
|
|
||||||
@ -3432,7 +3461,7 @@ g_type_init_with_debug_flags (GTypeDebugFlags debug_flags)
|
|||||||
|
|
||||||
/* G_TYPE_TYPE_PLUGIN
|
/* G_TYPE_TYPE_PLUGIN
|
||||||
*/
|
*/
|
||||||
g_type_plugin_get_type ();
|
votype = g_type_plugin_get_type ();
|
||||||
|
|
||||||
/* G_TYPE_* value types
|
/* G_TYPE_* value types
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user