mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-14 21:36:13 +01:00
Initialise the global GParamSpecPool in more places
Right now, we're assuming that GObjectClass will be initialised first and under a lock, but that's not always the case: when traversing a list of type, the first one might be a GTypeInterface, and if we initialise an interface that installs a property, the whole thing comes crashing down because the global GParamSpecPool is not initialised. Instead of taking a lock everywhere, we can use an atomic compare and swap; the first thread that installs a property wins the race, as any other access to the GParamSpecPool is performed under a lock.
This commit is contained in:
parent
8ce40ac590
commit
fc5f986e60
@ -403,6 +403,26 @@ _g_object_type_init (void)
|
||||
#endif /* G_ENABLE_DEBUG */
|
||||
}
|
||||
|
||||
/* Initialize the global GParamSpecPool; this function needs to be
|
||||
* called whenever we access the GParamSpecPool and we cannot guarantee
|
||||
* that g_object_do_class_init() has been called: for instance, by the
|
||||
* interface property API.
|
||||
*
|
||||
* To avoid yet another global lock, we use atomic pointer checks: the
|
||||
* first caller of this function will win the race. Any other access to
|
||||
* the GParamSpecPool is done under its own mutex.
|
||||
*/
|
||||
static inline void
|
||||
g_object_init_pspec_pool (void)
|
||||
{
|
||||
if (G_UNLIKELY (g_atomic_pointer_get (&pspec_pool) == NULL))
|
||||
{
|
||||
GParamSpecPool *pool = g_param_spec_pool_new (TRUE);
|
||||
if (!g_atomic_pointer_compare_and_exchange (&pspec_pool, NULL, pool))
|
||||
g_param_spec_pool_free (pool);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_object_base_class_init (GObjectClass *class)
|
||||
{
|
||||
@ -459,7 +479,8 @@ g_object_do_class_init (GObjectClass *class)
|
||||
#ifndef HAVE_OPTIONAL_FLAGS
|
||||
quark_in_construction = g_quark_from_static_string ("GObject-in-construction");
|
||||
#endif
|
||||
pspec_pool = g_param_spec_pool_new (TRUE);
|
||||
|
||||
g_object_init_pspec_pool ();
|
||||
|
||||
class->constructor = g_object_constructor;
|
||||
class->constructed = g_object_constructed;
|
||||
@ -525,6 +546,8 @@ install_property_internal (GType g_type,
|
||||
{
|
||||
g_param_spec_ref_sink (pspec);
|
||||
|
||||
g_object_init_pspec_pool ();
|
||||
|
||||
if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE))
|
||||
{
|
||||
g_critical ("When installing property: type '%s' already has a property named '%s'",
|
||||
@ -951,6 +974,8 @@ g_object_interface_find_property (gpointer g_iface,
|
||||
g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type), NULL);
|
||||
g_return_val_if_fail (property_name != NULL, NULL);
|
||||
|
||||
g_object_init_pspec_pool ();
|
||||
|
||||
return g_param_spec_pool_lookup (pspec_pool,
|
||||
property_name,
|
||||
iface_class->g_type,
|
||||
@ -1091,6 +1116,8 @@ g_object_interface_list_properties (gpointer g_iface,
|
||||
|
||||
g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type), NULL);
|
||||
|
||||
g_object_init_pspec_pool ();
|
||||
|
||||
pspecs = g_param_spec_pool_list (pspec_pool,
|
||||
iface_class->g_type,
|
||||
&n);
|
||||
|
Loading…
Reference in New Issue
Block a user