__sync_val_compare_and_swap vs __sync_bool_compare_and_swap

时间:2015-04-28 03:34:38

标签: c++ c gcc atomic built-in

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

首先,让我们有一个函数签名供参考(来自GCC docs减去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_val_compare_and_swap的实现方式,其中sync_bool_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;
}

正在将* ptr存储在ret中,因为* ptr可能会在调用__sync_bool_compare_and_swap之前发生变化。它让我意识到,就sync_bool_compare_and_swap而言,我似乎没有一种安全的方法(没有额外的障碍或锁定)来实现__sync_val_compare_and_swap。这让我觉得前者必须提供比后者更多的“信息”,但根据我的问题,我没有看到它确实存在。

1 个答案:

答案 0 :(得分:4)

__sync_val_compare_and_swap提供的操作始终可以__sync_bool_compare_and_swap实现(当然其他方向显然也可以),所以就 power 而言是等价的。但是,根据__sync_val_compare_and_swap实施__sync_bool_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。在某些方面,这是一个愚蠢的例子,因为它只是通过使用旧值来保存分支或条件移动,但是在其他情况下可能存在多个旧值并且知道导致操作失败的问题。