Merge branch 'ebassi/gtype-refcount' into 'main'

Drop TypeNode reference counting

See merge request GNOME/glib!3255
This commit is contained in:
Philip Withnall 2025-02-03 14:38:24 +00:00
commit cac9100960
2 changed files with 171 additions and 401 deletions

View File

@ -153,10 +153,6 @@ 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_U (TypeNode *node,
gboolean uncached);
static void type_data_last_unref_Wm (TypeNode * node,
gboolean uncached);
static inline gpointer type_get_qdata_L (TypeNode *node, static inline gpointer type_get_qdata_L (TypeNode *node,
GQuark quark); GQuark quark);
static inline void type_set_qdata_W (TypeNode *node, static inline void type_set_qdata_W (TypeNode *node,
@ -192,7 +188,6 @@ typedef enum
/* --- structures --- */ /* --- structures --- */
struct _TypeNode struct _TypeNode
{ {
guint ref_count; /* (atomic) */
#ifdef G_ENABLE_DEBUG #ifdef G_ENABLE_DEBUG
guint instance_count; /* (atomic) */ guint instance_count; /* (atomic) */
#endif #endif
@ -228,7 +223,6 @@ struct _TypeNode
#define NODE_PARENT_TYPE(node) (node->supers[1]) #define NODE_PARENT_TYPE(node) (node->supers[1])
#define NODE_FUNDAMENTAL_TYPE(node) (node->supers[node->n_supers]) #define NODE_FUNDAMENTAL_TYPE(node) (node->supers[node->n_supers])
#define NODE_NAME(node) (g_quark_to_string (node->qname)) #define NODE_NAME(node) (g_quark_to_string (node->qname))
#define NODE_REFCOUNT(node) ((guint) g_atomic_int_get ((int *) &(node)->ref_count))
#define NODE_IS_BOXED(node) (NODE_FUNDAMENTAL_TYPE (node) == G_TYPE_BOXED) #define NODE_IS_BOXED(node) (NODE_FUNDAMENTAL_TYPE (node) == G_TYPE_BOXED)
#define NODE_IS_IFACE(node) (NODE_FUNDAMENTAL_TYPE (node) == G_TYPE_INTERFACE) #define NODE_IS_IFACE(node) (NODE_FUNDAMENTAL_TYPE (node) == G_TYPE_INTERFACE)
#define CLASSED_NODE_IFACES_ENTRIES(node) (&(node)->_prot.iface_entries) #define CLASSED_NODE_IFACES_ENTRIES(node) (&(node)->_prot.iface_entries)
@ -297,7 +291,7 @@ struct _ClassData
CommonData common; CommonData common;
guint16 class_size; guint16 class_size;
guint16 class_private_size; guint16 class_private_size;
int init_state; /* (atomic) - g_type_class_ref reads it unlocked */ int init_state; /* (atomic) - g_type_class_get reads it unlocked */
GBaseInitFunc class_init_base; GBaseInitFunc class_init_base;
GBaseFinalizeFunc class_finalize_base; GBaseFinalizeFunc class_finalize_base;
GClassInitFunc class_init; GClassInitFunc class_init;
@ -311,7 +305,7 @@ struct _InstanceData
CommonData common; CommonData common;
guint16 class_size; guint16 class_size;
guint16 class_private_size; guint16 class_private_size;
int init_state; /* (atomic) - g_type_class_ref reads it unlocked */ int init_state; /* (atomic) - g_type_class_get reads it unlocked */
GBaseInitFunc class_init_base; GBaseInitFunc class_init_base;
GBaseFinalizeFunc class_finalize_base; GBaseFinalizeFunc class_finalize_base;
GClassInitFunc class_init; GClassInitFunc class_init;
@ -1204,7 +1198,7 @@ type_data_make_W (TypeNode *node,
!((G_TYPE_FLAG_VALUE_ABSTRACT | G_TYPE_FLAG_ABSTRACT) & !((G_TYPE_FLAG_VALUE_ABSTRACT | G_TYPE_FLAG_ABSTRACT) &
GPOINTER_TO_UINT (type_get_qdata_L (node, static_quark_type_flags)))); GPOINTER_TO_UINT (type_get_qdata_L (node, static_quark_type_flags))));
g_atomic_int_set ((int *) &node->ref_count, 1); g_assert (node->data->common.value_table != NULL); /* paranoid */
} }
static inline void static inline void
@ -1240,27 +1234,6 @@ type_data_ref_Wm (TypeNode *node)
check_value_table_I (NODE_NAME (node), check_value_table_I (NODE_NAME (node),
&tmp_value_table) ? &tmp_value_table : NULL); &tmp_value_table) ? &tmp_value_table : NULL);
} }
else
{
g_assert (NODE_REFCOUNT (node) > 0);
g_atomic_int_inc ((int *) &node->ref_count);
}
}
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 static gboolean
@ -1776,29 +1749,6 @@ type_iface_retrieve_holder_info_Wm (TypeNode *iface,
return iholder; /* we don't modify write lock upon returning NULL */ return iholder; /* we don't modify write lock upon returning NULL */
} }
static void
type_iface_blow_holder_info_Wm (TypeNode *iface,
GType instance_type)
{
IFaceHolder *iholder = iface_node_get_holders_L (iface);
g_assert (NODE_IS_IFACE (iface));
while (iholder->instance_type != instance_type)
iholder = iholder->next;
if (iholder->info && iholder->plugin)
{
g_free (iholder->info);
iholder->info = NULL;
G_WRITE_UNLOCK (&type_rw_lock);
g_type_plugin_unuse (iholder->plugin);
type_data_unref_U (iface, FALSE);
G_WRITE_LOCK (&type_rw_lock);
}
}
static void static void
maybe_issue_deprecation_warning (GType type) maybe_issue_deprecation_warning (GType type)
{ {
@ -1892,7 +1842,7 @@ g_type_create_instance (GType type)
maybe_issue_deprecation_warning (type); maybe_issue_deprecation_warning (type);
} }
class = g_type_class_ref (type); class = g_type_class_get (type);
/* We allocate the 'private' areas before the normal instance data, in /* We allocate the 'private' areas before the normal instance data, in
* reverse order. This allows the private area of a particular class * reverse order. This allows the private area of a particular class
@ -2035,8 +1985,6 @@ g_type_free_instance (GTypeInstance *instance)
g_atomic_int_add ((int *) &node->instance_count, -1); g_atomic_int_add ((int *) &node->instance_count, -1);
} }
#endif #endif
g_type_class_unref (class);
} }
static void static void
@ -2159,41 +2107,6 @@ type_iface_vtable_iface_init_Wm (TypeNode *iface,
} }
} }
static gboolean
type_iface_vtable_finalize_Wm (TypeNode *iface,
TypeNode *node,
GTypeInterface *vtable)
{
IFaceEntry *entry = type_lookup_iface_entry_L (node, iface);
IFaceHolder *iholder;
/* type_iface_retrieve_holder_info_Wm() doesn't modify write lock for returning NULL */
iholder = type_iface_retrieve_holder_info_Wm (iface, NODE_TYPE (node), FALSE);
if (!iholder)
return FALSE; /* we don't modify write lock upon FALSE */
g_assert (entry && entry->vtable == vtable && iholder->info);
entry->vtable = NULL;
entry->init_state = UNINITIALIZED;
if (iholder->info->interface_finalize || iface->data->iface.vtable_finalize_base)
{
G_WRITE_UNLOCK (&type_rw_lock);
if (iholder->info->interface_finalize)
iholder->info->interface_finalize (vtable, iholder->info->interface_data);
if (iface->data->iface.vtable_finalize_base)
iface->data->iface.vtable_finalize_base (vtable);
G_WRITE_LOCK (&type_rw_lock);
}
vtable->g_type = 0;
vtable->g_instance_type = 0;
g_free (vtable);
type_iface_blow_holder_info_Wm (iface, NODE_TYPE (node));
return TRUE; /* write lock modified */
}
static void static void
type_class_init_Wm (TypeNode *node, type_class_init_Wm (TypeNode *node,
GTypeClass *pclass) GTypeClass *pclass)
@ -2354,193 +2267,6 @@ type_class_init_Wm (TypeNode *node,
g_atomic_int_set (&node->data->class.init_state, INITIALIZED); g_atomic_int_set (&node->data->class.init_state, INITIALIZED);
} }
static void
type_data_finalize_class_ifaces_Wm (TypeNode *node)
{
guint i;
IFaceEntries *entries;
g_assert (node->is_instantiatable && node->data && node->data->class.class && NODE_REFCOUNT (node) == 0);
reiterate:
entries = CLASSED_NODE_IFACES_ENTRIES_LOCKED (node);
for (i = 0; entries != NULL && i < IFACE_ENTRIES_N_ENTRIES (entries); i++)
{
IFaceEntry *entry = &entries->entry[i];
if (entry->vtable)
{
if (type_iface_vtable_finalize_Wm (lookup_type_node_I (entry->iface_type), node, entry->vtable))
{
/* refetch entries, IFACES_ENTRIES might be modified */
goto reiterate;
}
else
{
/* type_iface_vtable_finalize_Wm() doesn't modify write lock upon FALSE,
* iface vtable came from parent
*/
entry->vtable = NULL;
entry->init_state = UNINITIALIZED;
}
}
}
}
static void
type_data_finalize_class_U (TypeNode *node,
ClassData *cdata)
{
GTypeClass *class = cdata->class;
TypeNode *bnode;
g_assert (cdata->class && NODE_REFCOUNT (node) == 0);
if (cdata->class_finalize)
cdata->class_finalize (class, (gpointer) cdata->class_data);
/* call all base class destruction functions in descending order
*/
if (cdata->class_finalize_base)
cdata->class_finalize_base (class);
for (bnode = lookup_type_node_I (NODE_PARENT_TYPE (node)); bnode; bnode = lookup_type_node_I (NODE_PARENT_TYPE (bnode)))
if (bnode->data->class.class_finalize_base)
bnode->data->class.class_finalize_base (class);
g_free (cdata->class);
}
static void
type_data_last_unref_Wm (TypeNode *node,
gboolean uncached)
{
g_return_if_fail (node != NULL && node->plugin != NULL);
if (!node->data || NODE_REFCOUNT (node) == 0)
{
g_critical ("cannot drop last reference to unreferenced type '%s'",
NODE_NAME (node));
return;
}
/* call class cache hooks */
if (node->is_classed && node->data && node->data->class.class && static_n_class_cache_funcs && !uncached)
{
guint i;
G_WRITE_UNLOCK (&type_rw_lock);
G_READ_LOCK (&type_rw_lock);
for (i = 0; i < static_n_class_cache_funcs; i++)
{
GTypeClassCacheFunc cache_func = static_class_cache_funcs[i].cache_func;
gpointer cache_data = static_class_cache_funcs[i].cache_data;
gboolean need_break;
G_READ_UNLOCK (&type_rw_lock);
need_break = cache_func (cache_data, node->data->class.class);
G_READ_LOCK (&type_rw_lock);
if (!node->data || NODE_REFCOUNT (node) == 0)
INVALID_RECURSION ("GType class cache function ", cache_func, NODE_NAME (node));
if (need_break)
break;
}
G_READ_UNLOCK (&type_rw_lock);
G_WRITE_LOCK (&type_rw_lock);
}
/* may have been re-referenced meanwhile */
if (g_atomic_int_dec_and_test ((int *) &node->ref_count))
{
GType ptype = NODE_PARENT_TYPE (node);
TypeData *tdata;
if (node->is_instantiatable)
{
/* destroy node->data->instance.mem_chunk */
}
tdata = node->data;
if (node->is_classed && tdata->class.class)
{
if (CLASSED_NODE_IFACES_ENTRIES_LOCKED (node) != NULL)
type_data_finalize_class_ifaces_Wm (node);
node->mutatable_check_cache = FALSE;
node->data = NULL;
G_WRITE_UNLOCK (&type_rw_lock);
type_data_finalize_class_U (node, &tdata->class);
G_WRITE_LOCK (&type_rw_lock);
}
else if (NODE_IS_IFACE (node) && tdata->iface.dflt_vtable)
{
node->mutatable_check_cache = FALSE;
node->data = NULL;
if (tdata->iface.dflt_finalize || tdata->iface.vtable_finalize_base)
{
G_WRITE_UNLOCK (&type_rw_lock);
if (tdata->iface.dflt_finalize)
tdata->iface.dflt_finalize (tdata->iface.dflt_vtable, (gpointer) tdata->iface.dflt_data);
if (tdata->iface.vtable_finalize_base)
tdata->iface.vtable_finalize_base (tdata->iface.dflt_vtable);
G_WRITE_LOCK (&type_rw_lock);
}
g_free (tdata->iface.dflt_vtable);
}
else
{
node->mutatable_check_cache = FALSE;
node->data = NULL;
}
/* freeing tdata->common.value_table and its contents is taken care of
* by allocating it in one chunk with tdata
*/
g_free (tdata);
G_WRITE_UNLOCK (&type_rw_lock);
g_type_plugin_unuse (node->plugin);
if (ptype)
type_data_unref_U (lookup_type_node_I (ptype), FALSE);
G_WRITE_LOCK (&type_rw_lock);
}
}
static inline void
type_data_unref_U (TypeNode *node,
gboolean uncached)
{
guint current;
do {
current = NODE_REFCOUNT (node);
if (current <= 1)
{
if (!node->plugin)
{
g_critical ("static type '%s' unreferenced too often",
NODE_NAME (node));
return;
}
else
{
/* This is the last reference of a type from a plugin. We are
* experimentally disabling support for unloading type
* plugins, so don't allow the last ref to drop.
*/
return;
}
g_assert (current > 0);
g_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, uncached);
G_WRITE_UNLOCK (&type_rw_lock);
g_rec_mutex_unlock (&class_init_rec_mutex);
return;
}
} while (!g_atomic_int_compare_and_exchange ((int *) &node->ref_count, current, current - 1));
}
/** /**
* g_type_add_class_cache_func: (skip) * g_type_add_class_cache_func: (skip)
* @cache_data: data to be passed to @cache_func * @cache_data: data to be passed to @cache_func
@ -2977,23 +2703,28 @@ g_type_add_interface_dynamic (GType instance_type,
/* --- public API functions --- */ /* --- public API functions --- */
/** /**
* g_type_class_ref: * g_type_class_get:
* @type: type ID of a classed type * @type: type ID of a classed type
* *
* Increments the reference count of the class structure belonging to * Retrieves the type class of the given @type.
* @type. This function will demand-create the class if it doesn't
* exist already.
* *
* Returns: (type GObject.TypeClass) (transfer none): the #GTypeClass * This function will create the class on demand if it does not exist
* structure for the given type ID * already.
*
* If you don't want to create the class, use g_type_class_peek() instead.
*
* Returns: (transfer none) (type GObject.TypeClass): the class structure
* for the type
*
* Since: 2.84
*/ */
gpointer gpointer
g_type_class_ref (GType type) g_type_class_get (GType type)
{ {
TypeNode *node; TypeNode *node;
GType ptype; GType ptype;
gboolean holds_ref;
GTypeClass *pclass; GTypeClass *pclass;
/* optimize for common code path */ /* optimize for common code path */
@ -3005,14 +2736,11 @@ g_type_class_ref (GType type)
return NULL; return NULL;
} }
if (G_LIKELY (type_data_ref_U (node))) if (G_LIKELY (node->data != NULL))
{ {
if (G_LIKELY (g_atomic_int_get (&node->data->class.init_state) == INITIALIZED)) if (G_LIKELY (g_atomic_int_get (&node->data->class.init_state) == INITIALIZED))
return node->data->class.class; return node->data->class.class;
holds_ref = TRUE;
} }
else
holds_ref = FALSE;
/* here, we either have node->data->class.class == NULL, or a recursive /* here, we either have node->data->class.class == NULL, or a recursive
* call to g_type_class_ref() with a partly initialized class, or * call to g_type_class_ref() with a partly initialized class, or
@ -3023,49 +2751,58 @@ g_type_class_ref (GType type)
/* we need an initialized parent class for initializing derived classes */ /* we need an initialized parent class for initializing derived classes */
ptype = NODE_PARENT_TYPE (node); ptype = NODE_PARENT_TYPE (node);
pclass = ptype ? g_type_class_ref (ptype) : NULL; pclass = ptype ? g_type_class_get (ptype) : NULL;
G_WRITE_LOCK (&type_rw_lock); G_WRITE_LOCK (&type_rw_lock);
if (!holds_ref) type_data_ref_Wm (node);
type_data_ref_Wm (node);
if (!node->data->class.class) /* class uninitialized */ if (!node->data->class.class) /* class uninitialized */
type_class_init_Wm (node, pclass); type_class_init_Wm (node, pclass);
G_WRITE_UNLOCK (&type_rw_lock); G_WRITE_UNLOCK (&type_rw_lock);
if (pclass)
g_type_class_unref (pclass);
g_rec_mutex_unlock (&class_init_rec_mutex); g_rec_mutex_unlock (&class_init_rec_mutex);
return node->data->class.class; return node->data->class.class;
} }
/**
* g_type_class_ref:
* @type: type ID of a classed type
*
* Increments the reference count of the class structure belonging to
* @type.
*
* This function will demand-create the class if it doesn't exist already.
*
* Returns: (type GObject.TypeClass) (transfer none): the #GTypeClass
* structure for the given type ID
*
* Deprecated: 2.84: Use g_type_class_get() instead
*/
gpointer
g_type_class_ref (GType type)
{
return g_type_class_get (type);
}
/** /**
* g_type_class_unref: * g_type_class_unref:
* @g_class: (type GObject.TypeClass): a #GTypeClass structure to unref * @g_class: (type GObject.TypeClass): a #GTypeClass structure to unref
* *
* Decrements the reference count of the class structure being passed in. * Decrements the reference count of the class structure being passed in.
*
* Once the last reference count of a class has been released, classes * Once the last reference count of a class has been released, classes
* may be finalized by the type system, so further dereferencing of a * may be finalized by the type system, so further dereferencing of a
* class pointer after g_type_class_unref() are invalid. * class pointer after g_type_class_unref() are invalid.
*
* Deprecated: 2.84: Type class reference counting has been removed and type
* classes now cannot be finalized. This function no longer does anything.
*/ */
void void
g_type_class_unref (gpointer g_class) g_type_class_unref (gpointer g_class)
{ {
TypeNode *node;
GTypeClass *class = g_class;
g_return_if_fail (g_class != NULL);
node = lookup_type_node_I (class->g_type);
if (node && node->is_classed && NODE_REFCOUNT (node))
type_data_unref_U (node, FALSE);
else
g_critical ("cannot unreference class of invalid (unclassed) type '%s'",
type_descriptive_name_I (class->g_type));
} }
/** /**
@ -3073,55 +2810,53 @@ g_type_class_unref (gpointer g_class)
* @g_class: (type GObject.TypeClass): a #GTypeClass structure to unref * @g_class: (type GObject.TypeClass): a #GTypeClass structure to unref
* *
* A variant of g_type_class_unref() for use in #GTypeClassCacheFunc * A variant of g_type_class_unref() for use in #GTypeClassCacheFunc
* implementations. It unreferences a class without consulting the chain * implementations.
*
* It unreferences a class without consulting the chain
* of #GTypeClassCacheFuncs, avoiding the recursion which would occur * of #GTypeClassCacheFuncs, avoiding the recursion which would occur
* otherwise. * otherwise.
*
* Deprecated: 2.84: Type class reference counting has been removed and type
* classes now cannot be finalized. This function no longer does anything.
*/ */
void void
g_type_class_unref_uncached (gpointer g_class) g_type_class_unref_uncached (gpointer g_class)
{ {
TypeNode *node;
GTypeClass *class = g_class;
g_return_if_fail (g_class != NULL);
node = lookup_type_node_I (class->g_type);
if (node && node->is_classed && NODE_REFCOUNT (node))
type_data_unref_U (node, TRUE);
else
g_critical ("cannot unreference class of invalid (unclassed) type '%s'",
type_descriptive_name_I (class->g_type));
} }
/** /**
* g_type_class_peek: * g_type_class_peek:
* @type: type ID of a classed type * @type: type ID of a classed type
* *
* This function is essentially the same as g_type_class_ref(), * Retrieves the class for a give type.
* except that the classes reference count isn't incremented. *
* This function is essentially the same as g_type_class_get(),
* except that the class may have not been instantiated yet.
*
* As a consequence, this function may return %NULL if the class * As a consequence, this function may return %NULL if the class
* of the type passed in does not currently exist (hasn't been * of the type passed in does not currently exist (hasn't been
* referenced before). * referenced before).
* *
* Returns: (type GObject.TypeClass) (transfer none): the #GTypeClass * Returns: (type GObject.TypeClass) (transfer none) (nullable): the
* structure for the given type ID or %NULL if the class does not * #GTypeClass structure for the given type ID or %NULL if the class
* currently exist * does not currently exist
*/ */
gpointer gpointer
g_type_class_peek (GType type) g_type_class_peek (GType type)
{ {
TypeNode *node; TypeNode *node;
gpointer class;
node = lookup_type_node_I (type); node = lookup_type_node_I (type);
if (node && node->is_classed && NODE_REFCOUNT (node) && if (node && node->is_classed)
g_atomic_int_get (&node->data->class.init_state) == INITIALIZED) {
/* ref_count _may_ be 0 */ if (node->data == NULL)
class = node->data->class.class; return NULL;
else
class = NULL; if (G_LIKELY (g_atomic_int_get (&node->data->class.init_state) == INITIALIZED))
return node->data->class.class;
}
return class; return NULL;
} }
/** /**
@ -3131,9 +2866,9 @@ g_type_class_peek (GType type)
* A more efficient version of g_type_class_peek() which works only for * A more efficient version of g_type_class_peek() which works only for
* static types. * static types.
* *
* Returns: (type GObject.TypeClass) (transfer none): the #GTypeClass * Returns: (type GObject.TypeClass) (transfer none) (nullable): the
* structure for the given type ID or %NULL if the class does not * #GTypeClass structure for the given type ID or %NULL if the class
* currently exist or is dynamically loaded * does not currently exist or is dynamically loaded
* *
* Since: 2.4 * Since: 2.4
*/ */
@ -3141,36 +2876,42 @@ gpointer
g_type_class_peek_static (GType type) g_type_class_peek_static (GType type)
{ {
TypeNode *node; TypeNode *node;
gpointer class;
node = lookup_type_node_I (type); node = lookup_type_node_I (type);
if (node && node->is_classed && NODE_REFCOUNT (node) && if (node && node->is_classed)
/* peek only static types: */ node->plugin == NULL && {
g_atomic_int_get (&node->data->class.init_state) == INITIALIZED) if (node->data == NULL)
/* ref_count _may_ be 0 */ return NULL;
class = node->data->class.class;
else /* peek only static types */
class = NULL; if (node->plugin != NULL)
return NULL;
return class;
if (G_LIKELY (g_atomic_int_get (&node->data->class.init_state) == INITIALIZED))
return node->data->class.class;
}
return NULL;
} }
/** /**
* g_type_class_peek_parent: * g_type_class_peek_parent:
* @g_class: (type GObject.TypeClass): the #GTypeClass structure to * @g_class: (type GObject.TypeClass): the #GTypeClass structure to
* retrieve the parent class for * retrieve the parent class for
*
* Retrieves the class structure of the immediate parent type of the
* class passed in.
* *
* This is a convenience function often needed in class initializers. * This is a convenience function often needed in class initializers.
* It returns the class structure of the immediate parent type of the *
* class passed in. Since derived classes hold a reference count on * Since derived classes hold a reference on their parent classes as
* their parent classes as long as they are instantiated, the returned * long as they are instantiated, the returned class will always exist.
* class will always exist.
* *
* This function is essentially equivalent to: * This function is essentially equivalent to:
* g_type_class_peek (g_type_parent (G_TYPE_FROM_CLASS (g_class))) * g_type_class_peek (g_type_parent (G_TYPE_FROM_CLASS (g_class)))
* *
* Returns: (type GObject.TypeClass) (transfer none): the parent class * Returns: (type GObject.TypeClass) (transfer none): the parent class
* of @g_class * of @g_class
*/ */
gpointer gpointer
g_type_class_peek_parent (gpointer g_class) g_type_class_peek_parent (gpointer g_class)
@ -3207,9 +2948,9 @@ g_type_class_peek_parent (gpointer g_class)
* Returns the #GTypeInterface structure of an interface to which the * Returns the #GTypeInterface structure of an interface to which the
* passed in class conforms. * passed in class conforms.
* *
* Returns: (type GObject.TypeInterface) (transfer none): the #GTypeInterface * Returns: (type GObject.TypeInterface) (transfer none) (nullable): the #GTypeInterface
* structure of @iface_type if implemented by @instance_class, %NULL * structure of @iface_type if implemented by @instance_class, %NULL
* otherwise * otherwise
*/ */
gpointer gpointer
g_type_interface_peek (gpointer instance_class, g_type_interface_peek (gpointer instance_class,
@ -3237,14 +2978,15 @@ g_type_interface_peek (gpointer instance_class,
* @g_iface: (type GObject.TypeInterface): a #GTypeInterface structure * @g_iface: (type GObject.TypeInterface): a #GTypeInterface structure
* *
* Returns the corresponding #GTypeInterface structure of the parent type * Returns the corresponding #GTypeInterface structure of the parent type
* of the instance type to which @g_iface belongs. This is useful when * of the instance type to which @g_iface belongs.
* deriving the implementation of an interface from the parent type and
* then possibly overriding some methods.
* *
* Returns: (transfer none) (type GObject.TypeInterface): the * This is useful when deriving the implementation of an interface from the
* corresponding #GTypeInterface structure of the parent type of the * parent type and then possibly overriding some methods.
* instance type to which @g_iface belongs, or %NULL if the parent *
* type doesn't conform to the interface * Returns: (transfer none) (type GObject.TypeInterface) (nullable): the
* corresponding #GTypeInterface structure of the parent type of the
* instance type to which @g_iface belongs, or %NULL if the parent
* type doesn't conform to the interface
*/ */
gpointer gpointer
g_type_interface_peek_parent (gpointer g_iface) g_type_interface_peek_parent (gpointer g_iface)
@ -3285,12 +3027,43 @@ g_type_interface_peek_parent (gpointer g_iface)
* *
* Since: 2.4 * Since: 2.4
* *
* Deprecated: 2.84: Use g_type_default_interface_get() instead
*
* Returns: (type GObject.TypeInterface) (transfer none): the default * Returns: (type GObject.TypeInterface) (transfer none): the default
* vtable for the interface; call g_type_default_interface_unref() * vtable for the interface; call g_type_default_interface_unref()
* when you are done using the interface. * when you are done using the interface.
*/ */
gpointer gpointer
g_type_default_interface_ref (GType g_type) g_type_default_interface_ref (GType g_type)
{
return g_type_default_interface_get (g_type);
}
/**
* g_type_default_interface_get:
* @g_type: an interface type
*
* Returns the default interface vtable for the given @g_type.
*
* If the type is not currently in use, then the default vtable
* for the type will be created and initialized by calling
* the base interface init and default vtable init functions for
* the type (the @base_init and @class_init members of #GTypeInfo).
*
* If you don't want to create the interface vtable, you should use
* g_type_default_interface_peek() instead.
*
* Calling g_type_default_interface_get() is useful when you
* want to make sure that signals and properties for an interface
* have been installed.
*
* Returns: (type GObject.TypeInterface) (transfer none): the default
* vtable for the interface.
*
* Since: 2.84
*/
gpointer
g_type_default_interface_get (GType g_type)
{ {
TypeNode *node; TypeNode *node;
gpointer dflt_vtable; gpointer dflt_vtable;
@ -3298,8 +3071,7 @@ g_type_default_interface_ref (GType g_type)
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_REFCOUNT (node) == 0))
{ {
G_WRITE_UNLOCK (&type_rw_lock); G_WRITE_UNLOCK (&type_rw_lock);
g_critical ("cannot retrieve default vtable for invalid or non-interface type '%s'", g_critical ("cannot retrieve default vtable for invalid or non-interface type '%s'",
@ -3317,8 +3089,6 @@ g_type_default_interface_ref (GType g_type)
type_iface_ensure_dflt_vtable_Wm (node); type_iface_ensure_dflt_vtable_Wm (node);
g_rec_mutex_unlock (&class_init_rec_mutex); g_rec_mutex_unlock (&class_init_rec_mutex);
} }
else
type_data_ref_Wm (node); /* ref_count >= 1 already */
dflt_vtable = node->data->iface.dflt_vtable; dflt_vtable = node->data->iface.dflt_vtable;
G_WRITE_UNLOCK (&type_rw_lock); G_WRITE_UNLOCK (&type_rw_lock);
@ -3336,8 +3106,8 @@ g_type_default_interface_ref (GType g_type)
* Since: 2.4 * Since: 2.4
* *
* Returns: (type GObject.TypeInterface) (transfer none): the default * Returns: (type GObject.TypeInterface) (transfer none): the default
* vtable for the interface, or %NULL if the type is not currently * vtable for the interface, or %NULL if the type is not currently
* in use * in use
*/ */
gpointer gpointer
g_type_default_interface_peek (GType g_type) g_type_default_interface_peek (GType g_type)
@ -3346,7 +3116,7 @@ g_type_default_interface_peek (GType g_type)
gpointer vtable; gpointer vtable;
node = lookup_type_node_I (g_type); node = lookup_type_node_I (g_type);
if (node && NODE_IS_IFACE (node) && NODE_REFCOUNT (node)) if (node && NODE_IS_IFACE (node) && node->data)
vtable = node->data->iface.dflt_vtable; vtable = node->data->iface.dflt_vtable;
else else
vtable = NULL; vtable = NULL;
@ -3360,38 +3130,33 @@ g_type_default_interface_peek (GType g_type)
* structure for an interface, as returned by g_type_default_interface_ref() * structure for an interface, as returned by g_type_default_interface_ref()
* *
* Decrements the reference count for the type corresponding to the * Decrements the reference count for the type corresponding to the
* interface default vtable @g_iface. If the type is dynamic, then * interface default vtable @g_iface.
* when no one is using the interface and all references have *
* been released, the finalize function for the interface's default * If the type is dynamic, then when no one is using the interface and all
* vtable (the @class_finalize member of #GTypeInfo) will be called. * references have been released, the finalize function for the interface's
* default vtable (the @class_finalize member of #GTypeInfo) will be called.
* *
* Since: 2.4 * Since: 2.4
*
* Deprecated: 2.84: Interface reference counting has been removed and
* interface types now cannot be finalized. This function no longer does
* anything.
*/ */
void void
g_type_default_interface_unref (gpointer g_iface) g_type_default_interface_unref (gpointer g_iface)
{ {
TypeNode *node;
GTypeInterface *vtable = g_iface;
g_return_if_fail (g_iface != NULL);
node = lookup_type_node_I (vtable->g_type);
if (node && NODE_IS_IFACE (node))
type_data_unref_U (node, FALSE);
else
g_critical ("cannot unreference invalid interface default vtable for '%s'",
type_descriptive_name_I (vtable->g_type));
} }
/** /**
* g_type_name: * g_type_name:
* @type: type to return name for * @type: type to return name for
* *
* Get the unique name that is assigned to a type ID. Note that this * Get the unique name that is assigned to a type ID.
* function (like all other GType API) cannot cope with invalid type *
* IDs. %G_TYPE_INVALID may be passed to this function, as may be any * Note that this function (like all other GType API) cannot cope with
* other validly registered type ID, but randomized type IDs should * invalid type IDs. %G_TYPE_INVALID may be passed to this function, as
* not be passed in and will most likely lead to a crash. * may be any other validly registered type ID, but randomized type IDs
* should not be passed in and will most likely lead to a crash.
* *
* Returns: (nullable): static type name or %NULL * Returns: (nullable): static type name or %NULL
*/ */
@ -4288,7 +4053,7 @@ type_check_is_value_type_U (GType type)
restart_check: restart_check:
if (node) if (node)
{ {
if (node->data && NODE_REFCOUNT (node) > 0 && if (node->data &&
node->data->common.value_table->value_init) node->data->common.value_table->value_init)
tflags = GPOINTER_TO_UINT (type_get_qdata_L (node, static_quark_type_flags)); tflags = GPOINTER_TO_UINT (type_get_qdata_L (node, static_quark_type_flags));
else if (NODE_IS_IFACE (node)) else if (NODE_IS_IFACE (node))
@ -4343,25 +4108,26 @@ g_type_check_value_holds (const GValue *value,
* that implements or has internal knowledge of the implementation of * that implements or has internal knowledge of the implementation of
* @type. * @type.
* *
* Returns: location of the #GTypeValueTable associated with @type or * Returns: (nullable) (transfer none): location of the #GTypeValueTable
* %NULL if there is no #GTypeValueTable associated with @type * associated with @type or %NULL if there is no #GTypeValueTable
* associated with @type
*/ */
GTypeValueTable* GTypeValueTable *
g_type_value_table_peek (GType type) g_type_value_table_peek (GType type)
{ {
GTypeValueTable *vtable = NULL; GTypeValueTable *vtable = NULL;
TypeNode *node = lookup_type_node_I (type); TypeNode *node = lookup_type_node_I (type);
gboolean has_refed_data, has_table; gboolean has_data, has_table;
if (node && NODE_REFCOUNT (node) && node->mutatable_check_cache) if (node != NULL && node->mutatable_check_cache)
return node->data->common.value_table; return node->data->common.value_table;
G_READ_LOCK (&type_rw_lock); G_READ_LOCK (&type_rw_lock);
restart_table_peek: restart_table_peek:
has_refed_data = node && node->data && NODE_REFCOUNT (node) > 0; has_data = node != NULL && node->data != NULL;
has_table = has_refed_data && node->data->common.value_table->value_init; has_table = has_data && node->data->common.value_table->value_init;
if (has_refed_data) if (has_data)
{ {
if (has_table) if (has_table)
vtable = node->data->common.value_table; vtable = node->data->common.value_table;
@ -4391,7 +4157,7 @@ g_type_value_table_peek (GType type)
if (!node) if (!node)
g_critical (G_STRLOC ": type id '%" G_GUINTPTR_FORMAT "' is invalid", (guintptr) type); g_critical (G_STRLOC ": type id '%" G_GUINTPTR_FORMAT "' is invalid", (guintptr) type);
if (!has_refed_data) if (!has_data)
g_critical ("can't peek value table for type '%s' which is not currently referenced", g_critical ("can't peek value table for type '%s' which is not currently referenced",
type_descriptive_name_I (type)); type_descriptive_name_I (type));
@ -5039,7 +4805,7 @@ g_type_class_get_private (GTypeClass *klass,
if (NODE_PARENT_TYPE (private_node)) if (NODE_PARENT_TYPE (private_node))
{ {
parent_node = lookup_type_node_I (NODE_PARENT_TYPE (private_node)); parent_node = lookup_type_node_I (NODE_PARENT_TYPE (private_node));
g_assert (parent_node->data && NODE_REFCOUNT (parent_node) > 0); g_assert (parent_node->data);
if (G_UNLIKELY (private_node->data->class.class_private_size == parent_node->data->class.class_private_size)) if (G_UNLIKELY (private_node->data->class.class_private_size == parent_node->data->class.class_private_size))
{ {

View File

@ -759,6 +759,8 @@ gboolean g_type_is_a (GType type,
/* Hoist exact GType comparisons into the caller */ /* Hoist exact GType comparisons into the caller */
#define g_type_is_a(a,b) ((a) == (b) || (g_type_is_a) ((a), (b))) #define g_type_is_a(a,b) ((a) == (b) || (g_type_is_a) ((a), (b)))
GOBJECT_AVAILABLE_IN_2_84
gpointer g_type_class_get (GType type);
GOBJECT_AVAILABLE_IN_ALL GOBJECT_AVAILABLE_IN_ALL
gpointer g_type_class_ref (GType type); gpointer g_type_class_ref (GType type);
GOBJECT_AVAILABLE_IN_ALL GOBJECT_AVAILABLE_IN_ALL
@ -775,6 +777,8 @@ gpointer g_type_interface_peek (gpointer instance_
GOBJECT_AVAILABLE_IN_ALL GOBJECT_AVAILABLE_IN_ALL
gpointer g_type_interface_peek_parent (gpointer g_iface); gpointer g_type_interface_peek_parent (gpointer g_iface);
GOBJECT_AVAILABLE_IN_2_84
gpointer g_type_default_interface_get (GType g_type);
GOBJECT_AVAILABLE_IN_ALL GOBJECT_AVAILABLE_IN_ALL
gpointer g_type_default_interface_ref (GType g_type); gpointer g_type_default_interface_ref (GType g_type);
GOBJECT_AVAILABLE_IN_ALL GOBJECT_AVAILABLE_IN_ALL