atomics: Use ACQUIRE ordering for ref_cur()

Before, `ref_cur()` used RELAXED memory ordering, which is sufficient
for diagnostic reads but provides no ordering guarantees against
concurrent `ref_put()` operations on other threads.  Since `ref_put()`
already uses ACQ_REL ordering, readers should use ACQUIRE ordering
so that observing a given refcount value (particularly zero) also
makes all prior stores by the releasing thread visible.

There is no significant performance impact as on x86 ACQUIRE loads
compile to the same instruction as RELAXED loads.  But this fixes
potential issues on weakly-ordered architectures (e.g. ARM).

The __sync* and spinlock fallbacks already provide full ordering (they
might not actually be necessary anymore nowadays).
This commit is contained in:
Tobias Brunner
2026-07-24 08:47:36 +02:00
parent 6b58665697
commit bcef2c8f01
+3 -1
View File
@@ -52,7 +52,9 @@ typedef u_int refcount_t;
* __ATOMIC_ACQUIRE if we reach 0, but since we don't have control over the use
* of ref_put() we have to make sure. */
#define ref_put(ref) (!__atomic_sub_fetch(ref, 1, __ATOMIC_ACQ_REL))
#define ref_cur(ref) __atomic_load_n(ref, __ATOMIC_RELAXED)
/* Use __ATOMIC_ACQUIRE here so a thread checking for 0 to e.g. reuse an unused
* object sees writes prior to ref_put() that set the counter to 0. */
#define ref_cur(ref) __atomic_load_n(ref, __ATOMIC_ACQUIRE)
#define _cas_impl(ptr, oldval, newval) ({ typeof(*ptr) _old = oldval; \
__atomic_compare_exchange_n(ptr, &_old, newval, FALSE, \