gobject: shrink WeakRefStack during release-all

It feels ugly to leave the buffer not sized right.

We call g_object_weak_release_all() during g_object_real_dispose() and
right before finalize. In most cases, we expect that the loop iterates
until there are no weak notifications left (in which case the entire
WeakRefStack is freed). In that case, there is no need to shrink the
buffer, because it's going to be released soon anyway.

Note that no new weak references can be registered after finalize (as
the ref count already dropped to zero). However, new weak referenes can
be registered during dispose (either during the last g_object_unref() or
during g_object_run_dispose()).

In that case, I feel it is nice to bring the buffer size right again. We
don't know how long the object will continue to live afterwards, so
let's trim the extra allocation.
This commit is contained in:
Thomas Haller
2025-04-23 08:17:13 +02:00
parent dd9c3d4362
commit 9b303ee5b3

View File

@@ -3753,6 +3753,38 @@ _weak_ref_stack_free (WeakRefStack *wstack)
g_free (wstack);
}
G_ALWAYS_INLINE static inline gboolean
_weak_ref_stack_needs_shrink (WeakRefStack *wstack)
{
return wstack->n_weak_refs <= wstack->alloc_size / 4u;
}
G_ALWAYS_INLINE static inline void
_weak_ref_stack_maybe_shrink (WeakRefStack **p_wstack, gpointer *data)
{
WeakRefStack *wstack = *p_wstack;
#ifdef G_ENABLE_DEBUG
g_assert (wstack->n_weak_refs > 0);
#endif
if (!G_UNLIKELY (_weak_ref_stack_needs_shrink (wstack)))
return;
/* There is a loop here to find the right allocation size, because during
* g_object_weak_release_all() we don't shrink every time an entry is
* removed. */
do
{
wstack->alloc_size = wstack->alloc_size / 2u;
}
while (G_UNLIKELY (_weak_ref_stack_needs_shrink (wstack)));
wstack = g_realloc (wstack, WEAK_REF_STACK_ALLOC_SIZE (wstack->alloc_size));
*data = wstack;
*p_wstack = wstack;
}
G_ALWAYS_INLINE static inline void
_weak_ref_stack_update_release_all_state (WeakRefStack *wstack, guint idx)
{
@@ -3917,12 +3949,7 @@ handle_weak_ref_found:
sizeof (wstack->weak_refs[idx]) * (wstack->n_weak_refs - idx));
}
if (G_UNLIKELY (wstack->n_weak_refs <= wstack->alloc_size / 4u))
{
wstack->alloc_size = wstack->alloc_size / 2u;
wstack = g_realloc (wstack, WEAK_REF_STACK_ALLOC_SIZE (wstack->alloc_size));
*data = wstack;
}
_weak_ref_stack_maybe_shrink (&wstack, data);
}
return NULL;
@@ -4049,8 +4076,14 @@ g_object_weak_release_all_cb (gpointer *data,
&wstack->weak_refs[1],
sizeof (wstack->weak_refs[0]) * wstack->n_weak_refs);
/* Don't bother to shrink the buffer. Most likely the object gets
* destroyed soon after. */
if (wdata->release_all_done)
{
/* We maybe-shrink on each g_object_weak_unref(). During release-all,
* we usually don't shrink, because we expect that we pop all entries.
* In this case, we are now done and additional entries were subscribed
* in the meantime. In this case also maybe-shrink. */
_weak_ref_stack_maybe_shrink (&wstack, data);
}
}
return wdata;