From 3566aeb079087756bd4990617f73910e08b8a172 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 24 Sep 2024 18:24:55 -0700 Subject: [PATCH] glib/gvariant: skip bitlock for g_variant_ref_sink() In almost all cases, the variant is sunk shortly after creation and certainly before other threads get a view of it. This skips the bitlock for STATE_FLOATING, which is also done in g_variant_take_ref(). We can do this because STATE_FLOATING may only be set up-front, never after being cleared. That allows us to do a relaxed read of the value once (which if it is zero means no additional atomics beyond our ref count increment) as well as a single atomic if we do in fact steal the floating reference. Without any other GVariant performance changes, this is in the 2% range of benchmarking a tight loop using GVariantBuilder. However, after the rest of them are applied, the percentage is greater due to reduced runtime overhead and lands in the 4.5% range. --- glib/gvariant-core.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c index eb25d768d..fb5e2969d 100644 --- a/glib/gvariant-core.c +++ b/glib/gvariant-core.c @@ -863,19 +863,24 @@ g_variant_ref (GVariant *value) GVariant * g_variant_ref_sink (GVariant *value) { + int old_state; + g_return_val_if_fail (value != NULL, NULL); g_return_val_if_fail (!g_atomic_ref_count_compare (&value->ref_count, 0), NULL); - g_variant_lock (value); - TRACE(GLIB_VARIANT_REF_SINK(value, value->type_info, value->ref_count, value->state, value->state & STATE_FLOATING)); - if (~value->state & STATE_FLOATING) - g_variant_ref (value); - else - value->state &= ~STATE_FLOATING; + old_state = value->state; - g_variant_unlock (value); + while (old_state & STATE_FLOATING) + { + int new_state = old_state & ~STATE_FLOATING; + + if (g_atomic_int_compare_and_exchange_full (&value->state, old_state, new_state, &old_state)) + return value; + } + + g_atomic_ref_count_inc (&value->ref_count); return value; }