gclosure: Split out invalidation to a helper function

This avoids the need to ref/unref the closure while invalidating it in
the `closure->ref_count == 1` path in `g_closure_unref()`.

scan-build gets very confused about the ref count here, and ends up
assuming it’s possible for the `g_closure_unref()` call in
`g_closure_invalidate()` to finalise the closure when the latter is
called from `g_closure_unref()`. There was an existing assertion in
`g_closure_invalidate()` which hinted that this wasn’t possible, but
scan-build doesn’t seem to be able to propagate assumptions about
refcounts between function contexts.

So, introduce an internal variant of `g_closure_invalidate()` which can
skip modifying the closure’s refcount. It’s safe to invalidate the
closure without adding a ref when doing so from `g_closure_unref()` with
`closure->ref_count == 1` because at that point `g_closure_unref()`
holds the only remaining ref to the closure. So none of the invalidation
callbacks are allowed to unref it further.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>

Helps: #1767
This commit is contained in:
Philip Withnall 2024-04-09 23:49:37 +01:00
parent 4894168631
commit e41b4b1acb
No known key found for this signature in database
GPG Key ID: DCDF5885B1F3ED73

View File

@ -567,6 +567,17 @@ g_closure_ref (GClosure *closure)
return closure;
}
static void
closure_invalidate_internal (GClosure *closure)
{
gboolean was_invalid;
SWAP (closure, is_invalid, TRUE, &was_invalid);
/* invalidate only once */
if (!was_invalid)
closure_invoke_notifiers (closure, INOTIFY);
}
/**
* g_closure_invalidate:
* @closure: #GClosure to invalidate
@ -594,12 +605,8 @@ g_closure_invalidate (GClosure *closure)
if (!closure->is_invalid)
{
gboolean was_invalid;
g_closure_ref (closure); /* preserve floating flag */
SWAP (closure, is_invalid, TRUE, &was_invalid);
/* invalidate only once */
if (!was_invalid)
closure_invoke_notifiers (closure, INOTIFY);
closure_invalidate_internal (closure);
g_closure_unref (closure);
}
}
@ -622,8 +629,9 @@ g_closure_unref (GClosure *closure)
g_return_if_fail (closure != NULL);
g_return_if_fail (closure->ref_count > 0);
if (closure->ref_count == 1) /* last unref, invalidate first */
g_closure_invalidate (closure);
/* last unref, invalidate first */
if (closure->ref_count == 1 && !closure->is_invalid)
closure_invalidate_internal (closure);
DEC_ASSIGN (closure, ref_count, &new_ref_count);