i've been thinking return values of these 2 functions. __sync_bool_compare_and_swap function's return value seems have obvious benefits, i.e. can use tell whether swap operation took place. can't see use of __sync_val_compare_and_swap's return value.
firstly, lets have function signature reference (from gcc docs minus var args):
type __sync_val_compare_and_swap (type *ptr, type oldval type newval);
the problem see return value of __sync_val_compare_and_swap old value of *ptr. precise, it's value seen implementation of function once appropriate memory barriers had been put in place. explicitly state cater fact between calling __sync_val_compare_and_swap , executing instructions enforce memory barrier value of *ptr change.
now, when function returns can return value? there's no point trying compare *ptr because *ptr can changed on other threads. likewise comparing newval , *ptr doesn't me either (unless lock *ptr undermines use of atomics in first place).
so that's left me ask whether return value == oldval, (see below caveat) asking whether swap operation took place. have used __sync_bool_compare_and_swap.
the caveat mentioned subtle difference can see here doing doesn't tell me whether swap occured or not, tells me @ point before memory barrier released *ptr had same value newval. i'm considering possibility oldval == newval (although i'd struggle see way of implementing function efficiently check these values first , not swap if same it's moot point). can't see situation knowing difference make difference me @ call site. in fact, can't imagine situation set oldval , newval equal.
my question thus:
is there use case in using __sync_val_compare_and_swap , __sync_bool_compare_and_swap not equivalent, i.e. there situation 1 provides more information other?
aside
the reason thinking found implementation of __sync_val_compare_and_swap in terms of sync_bool_compare_and_swap has race:
inline int32_t __sync_val_compare_and_swap(volatile int32_t* ptr, int32_t oldval, int32_t newval) { int32_t ret = *ptr; (void)__sync_bool_compare_and_swap(ptr, oldval, newval); return ret; }
the race being on storing of *ptr in ret, *ptr change before __sync_bool_compare_and_swap called. made me realise there doesn't seem safe way (without barriers or locks) of implementing __sync_val_compare_and_swap in terms of sync_bool_compare_and_swap. got me thinking former must provide more "information" latter, per question don't see does.
the operation provided __sync_val_compare_and_swap
can implemented in terms of __sync_bool_compare_and_swap
(and of course other direction possible), in terms of power 2 equivalent. implementing __sync_val_compare_and_swap
in terms of __sync_bool_compare_and_swap
not efficient. looks like:
for (;;) { bool success = __sync_bool_compare_and_swap(ptr, oldval, newval); if (success) return oldval; type tmp = *ptr; __sync_synchronize(); if (tmp != oldval) return tmp; }
the work needed because observe failure of __sync_bool_compare_and_swap
read new value *ptr
happens match oldval
.
as why might prefer __sync_val_compare_and_swap
behavior, value caused failure may give starting point retry operation more efficiently or might indicate meaningful cause of failure operation won't "retried". example, see code pthread_spin_trylock
in musl libc (for author):
there a_cas
equivalent __sync_val_compare_and_swap
. in ways stupid example since it's saving branch or conditional move using old value, there other situations multiple old values possible , knowing 1 caused operation fail matters.