__sync_val_compare_and_swap vs __sync_bool_compare_and_swap

__sync_val_compare_and_swap vs __sync_bool_compare_and_swap

本文关键字:and swap compare sync bool vs val      更新时间:2023-10-16

我一直在考虑这两个函数的返回值。__sync_bool_compare_and_swap函数的返回值似乎有明显的好处,即我可以用它来判断交换操作是否发生。但是,我看不出__sync_val_compare_and_swap的返回值有多大用处。

首先,让我们有一个函数签名供参考(来自GCC文档减去var args):

type __sync_val_compare_and_swap (type *ptr, type oldval type newval);

我看到的问题是__sync_val_compare_and_swap的返回值是*ptr的旧值。准确地说,这是在设置了适当的内存屏障后,该函数的实现所看到的值。我明确指出这一点是为了满足这样一个事实,即在调用__sync_val_compare_and_swap和执行指令以强制执行内存屏障之间,*ptr的值可以很容易地更改。

现在,当函数返回时,我该如何处理该返回值?尝试将它与*ptr进行比较是没有意义的,因为*ptr现在可以在其他线程上更改。同样,比较newval和*ptr对我也没有真正的帮助(除非我锁定*ptr,这可能会首先破坏我对原子的使用)。

因此,我真正要做的就是询问返回值是否=oldval,这实际上是在询问交换操作是否发生。所以我本可以使用__sync_bool_compare_and_swap。

我刚才提到的警告是,我在这里看到的唯一细微的区别是,这样做并不能告诉我是否发生了交换,它只是"告诉"我在内存屏障释放之前的某个时刻*ptr与newval具有相同的值。我正在考虑oldval==newval的可能性(尽管我很难找到一种有效实现函数的方法,这样它就可以首先检查这些值,如果它们相同,就不会交换,所以这可能是一个没有意义的问题)。然而,在呼叫现场,我看不出知道这种差异会对我产生什么影响。事实上,我无法想象我会将oldval和newval设置为相等的情况。

因此,我的问题是:

是否存在使用__sync_val_compare_and_swap和__sync_bool_compare-and_swap不等效的用例,即是否存在一个提供的信息多于另一个的情况

ASIDE

我之所以考虑这一点,是因为我发现了一个根据sync_bool_compare_and_swap实现的__sync_val_compare-and_swap,它有一个种族:

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

竞争是在ret中存储*ptr,因为*ptr可能在调用__sync_bool_compare_and_swap之前发生更改。这让我意识到,在sync_bool_compare_and_swap方面,似乎没有一种安全的方法(没有额外的屏障或锁)来实现__sync_val_compare-and_swap。这让我想到前者必须比后者提供更多的"信息",但根据我的问题,我认为它并没有真正提供信息。

__sync_val_compare_and_swap提供的操作总是可以根据__sync_bool_compare_and_swap来实现(当然,另一个方向显然是可能的),因此就功率而言,两者是等价的。然而,根据__sync_bool_compare_and_swap来实现__sync_val_compare_and_swap不是很有效。它看起来像:

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

需要额外的工作,因为您可以观察到__sync_bool_compare_and_swap的故障,但随后从*ptr中读取恰好与oldval匹配的新值。

至于为什么您可能更喜欢__sync_val_compare_and_swap行为,导致失败的值可能会为您提供一个更有效地重试操作的起点,或者可能指示某个不会"重试"的操作失败的有意义的原因。例如,请参阅musl-libc中pthread_spin_trylock的代码(我是该代码的作者):

http://git.musl-libc.org/cgit/musl/tree/src/thread/pthread_spin_trylock.c?id=afbcac6826988d12d9a874359cab735049c17500

其中a_cas相当于__sync_val_compare_and_swap。在某些方面,这是一个愚蠢的例子,因为它只是通过使用旧值来保存分支或条件移动,但在其他情况下,可能存在多个旧值,并且知道导致操作失败的那个值很重要。