Merge branch 'th/gobj-empty-notify-queue' into 'main'

[th/gobj-empty-notify-queue] gobject: optimize notify-queue handling for a single freeze

See merge request GNOME/glib!4642
This commit is contained in:
Philip Withnall
2025-05-23 05:44:43 +00:00

View File

@@ -599,6 +599,26 @@ weak_ref_data_has (GObject *object, WeakRefData *wrdata, WeakRefData **out_new_w
/* --- functions --- */
static const GObjectNotifyQueue notify_queue_empty = {
.freeze_count = 0,
};
G_ALWAYS_INLINE static inline gboolean
_is_notify_queue_empty (const GObjectNotifyQueue *nqueue)
{
/* Only the notify_queue_empty instance has a zero freeze count. We check
* here for that condition instead of pointer comparing to
* &notify_queue_empty. That seems better because callers will afterwards
* dereference "freeze_count", so the value is already loaded.
*
* In any case, both conditions must be equivalent.
*/
#ifdef G_ENABLE_DEBUG
g_assert ((nqueue == &notify_queue_empty) == (nqueue->freeze_count == 0));
#endif
return nqueue->freeze_count == 0;
}
G_ALWAYS_INLINE static inline gsize
g_object_notify_queue_alloc_size (gsize alloc)
{
@@ -630,9 +650,10 @@ g_object_notify_queue_freeze_cb (gpointer *data,
if (!nqueue)
{
/* The nqueue doesn't exist yet. We create it, and freeze thus 1 time. */
*data = g_object_notify_queue_new_frozen ();
*destroy_notify = g_free;
/* The nqueue doesn't exist yet. We use the dummy object that is shared
* by all instances. */
*data = (gpointer) &notify_queue_empty;
*destroy_notify = NULL;
}
else if (!freeze_always)
{
@@ -643,7 +664,14 @@ g_object_notify_queue_freeze_cb (gpointer *data,
}
else
{
if (G_UNLIKELY (nqueue->freeze_count == G_MAXUINT16))
if (_is_notify_queue_empty (nqueue))
{
nqueue = g_object_notify_queue_new_frozen ();
*data = nqueue;
*destroy_notify = g_free;
nqueue->freeze_count++;
}
else if (G_UNLIKELY (nqueue->freeze_count == G_MAXUINT16))
{
g_critical ("Free queue for %s (%p) is larger than 65535,"
" called g_object_freeze_notify() too often."
@@ -674,19 +702,27 @@ g_object_notify_queue_thaw_cb (gpointer *data,
GObject *object = user_data;
GObjectNotifyQueue *nqueue = *data;
if (G_UNLIKELY (!nqueue || nqueue->freeze_count == 0))
if (G_UNLIKELY (!nqueue))
{
g_critical ("%s: property-changed notification for %s(%p) is not frozen",
G_STRFUNC, G_OBJECT_TYPE_NAME (object), object);
return NULL;
}
if (_is_notify_queue_empty (nqueue))
{
*data = NULL;
*destroy_notify = NULL;
return NULL;
}
nqueue->freeze_count--;
if (nqueue->freeze_count > 0)
return NULL;
*data = NULL;
*destroy_notify = NULL;
return nqueue;
}
@@ -762,6 +798,12 @@ g_object_notify_queue_add_cb (gpointer *data,
*data = nqueue;
*destroy_notify = g_free;
}
else if (_is_notify_queue_empty (nqueue))
{
nqueue = g_object_notify_queue_new_frozen ();
*data = nqueue;
*destroy_notify = g_free;
}
else
{
for (i = 0; i < nqueue->len; i++)
@@ -4479,6 +4521,7 @@ toggle_refs_unref_cb (gpointer *data,
g_datalist_unset_flags (&trdata->object->qdata, OBJECT_HAS_TOGGLE_REF_FLAG);
g_free (tstack);
*data = NULL;
*destroy_notify = NULL;
}
else if (i != tstack->n_toggle_refs)
tstack->toggle_refs[i] = tstack->toggle_refs[tstack->n_toggle_refs];