mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-14 13:26:16 +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,11 +546,13 @@ 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'",
|
||||
g_type_name (g_type),
|
||||
pspec->name);
|
||||
g_type_name (g_type),
|
||||
pspec->name);
|
||||
g_param_spec_unref (pspec);
|
||||
return FALSE;
|
||||
}
|
||||
@ -950,7 +973,9 @@ 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,
|
||||
@ -1076,10 +1101,10 @@ g_object_class_list_properties (GObjectClass *class,
|
||||
* Since: 2.4
|
||||
*
|
||||
* Returns: (array length=n_properties_p) (transfer container): a
|
||||
* pointer to an array of pointers to #GParamSpec
|
||||
* structures. The paramspecs are owned by GLib, but the
|
||||
* array should be freed with g_free() when you are done with
|
||||
* it.
|
||||
* pointer to an array of pointers to #GParamSpec
|
||||
* structures. The paramspecs are owned by GLib, but the
|
||||
* array should be freed with g_free() when you are done with
|
||||
* it.
|
||||
*/
|
||||
GParamSpec**
|
||||
g_object_interface_list_properties (gpointer g_iface,
|
||||
@ -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