99网
您的当前位置:首页CAS 原子比较交换操作

CAS 原子比较交换操作

来源:99网

原子比较交换操作

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 == *expectedstrex 失败

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;

/* raw refcnt no release-function */
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; /* this is atomic and seq_cst */
}

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 new counter */
			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 new counter */
			return old + 1;
		}
	}
	__builtin_unreachable();
}

因篇幅问题不能全部显示,请点此查看更多更全内容