CAS 原子比较交换操作

原子比较交换操作

C111

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 失败

因此外层函数不应当依赖 *expected 的值来判断成功与否。

在这里插入图片描述
尝试把 expected 返回回来看下是否有差异

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;
}

在这里插入图片描述
这里有一个坑就是没有判断 strex 的操作结果,前面提到了这个操作是可能失败的。因此比较操作成功而交换操作失败时,外层的函数如果以 old_v 作为操作成功的依据,会误以为成功了。此时得到的返回值始终是读取到的当前 *obj。外层函数不应当依赖 *expected 的值来判断操作是否成功。

因此正确的写法可以是

#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();
}

  1. https://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值