原子比较交换操作
C11
Atomically compares the object representation (until C++20)value representation (since C++20) of the object pointed to by obj with that of the object pointed to by expected, and if those are bitwise-equal, replaces the former with desired (performs read-modify-write operation). Otherwise, loads the actual value pointed to by obj into *expected (performs load operation). Copying is performed as if by std::memcpy.
The memory models for the read-modify-write and load operations are succ and fail respectively. The (1-2) versions use std::memory_order_seq_cst by default.
compare_exchange(*obj, *expected, desired)
The result of the comparison: true if *obj was equal to *expected, false otherwise.
接口操作是比较并交换。
如果 *obj == *expected,则更新 *obj 到值 desired,*expected 不变。否则将 *expected 更新为当前读取到的 *obj 值。但是实际情况并不和文档描述得那么一致,特别是 *expected 的更新问题。
atomic_compare_exchange_weak
The weak forms of the functions are allowed to fail spuriously, that is, act as if *obj != *expected even if they are equal. When a compare-and-exchange is in a loop, the weak version will yield better performance on some platforms.
int raw_atomic_int_cas_weak(raw_atomic_int_t *v, int old_v, int new_v)
{
return atomic_compare_exchange_weak(v, &old_v, new_v);
}
通过看指令(armeb),ip 就是 r12。我们可以看到 strex 是可以失败的,r3 表示 strex 操作成功与否,0 表示成功,成功则返回 true。可以注意到失败时不会更新 *expected 的值,始终是第一次读取到的 *expected。这与文档描述是有出入的。失败场景为:
*obj != *expected*obj == *expected 而 strex 失败
int raw_atomic_int_cas_weak_with_old_val(raw_atomic_int_t *v, int old_v, int new_v)
{
atomic_compare_exchange_weak(v, &old_v, new_v);
return old_v;
}
因此正确的写法可以是
#include <stdatomic.h>
typedef atomic_int raw_atomic_int_t;
struct raw_refcnt_nr {
raw_atomic_int_t count;
};
int raw_atomic_int_cas_weak(raw_atomic_int_t *v, int old_v, int new_v)
{
return atomic_compare_exchange_weak(v, &old_v, new_v);
}
static inline int raw_atomic_int_read(raw_atomic_int_t *v)
{
return *v;
}
int raw_refcnt_nr_get(struct raw_refcnt_nr *ref_nr)
{
int old_val, success;
while (1) {
old_val = raw_atomic_int_read(&ref_nr->count);
success = raw_atomic_int_cas_weak(&ref_nr->count, old_val, old_val + 1);
if (success) {
return old_val + 1;
}
}
__builtin_unreachable();
}
如果希望返回 old_val 来判断成功与否,则可以这么写。
int raw_atomic_int_cas(raw_atomic_int_t *v, int old_v, int new_v)
{
int success;
volatile int old = old_v;
do {
success = atomic_compare_exchange_weak(v, &old_v, new_v);
} while (success == 0 && old_v == old);
return old_v;
}
int raw_refcnt_nr_get(struct raw_refcnt_nr *ref_nr)
{
int old, ok;
while (1) {
old = raw_atomic_int_read(&ref_nr->count);
ok = raw_atomic_int_cas(&ref_nr->count, old, old + 1);
if (ok == old && ok >= 0) {
return old + 1;
}
}
__builtin_unreachable();
}