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:
@@ -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, \
|
||||
|
||||
Reference in New Issue
Block a user