__sync_val_compare_and_swap与__sync_bool_compare_and_swap [英] __sync_val_compare_and_swap vs __sync_bool_compare_and_swap

查看:842
本文介绍了__sync_val_compare_and_swap与__sync_bool_compare_and_swap的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

p>

  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似乎并不是一种安全的方式(没有额外的障碍或锁定)。这让我想到前者必须提供比后者更多的信息,但根据我的问题,我没有看到它确实如此。

解决方案 __ sync_val_compare_and_swap 提供的操作总是可以用 __ sync_bool_compare_and_swap 来实现(当然也可以另一个方向显然是可能的),所以就 power 而言,两者是等价的。但是,按照 __ 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;



$ b $ p
$ b

额外的工作是必需的,因为你可以观察 __ sync_bool_compare_and_swap ,然后从 * ptr 中读取一个恰好匹配 oldval 的新值。



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

< a href =http://git.musl-libc.org/cgit/musl/tree/src/thread/pthread_spin_trylock.c?id=afbcac6826988d12d9a874359cab735049c17500 =nofollow> http://git.musl-libc。 org / cgit / musl / tree / src / thread / pthread_spin_trylock.c?id = afbcac6826988d12d9a874359cab735049c17500



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


I've been thinking about the return values of these two functions. The __sync_bool_compare_and_swap function's return value seems to have obvious benefits, i.e. I can use it to tell whether the swap operation took place. However I can't see a good use of __sync_val_compare_and_swap's return value.

Firstly, lets have a function signature for reference (from GCC docs minus the var args):

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

The problem I see is that the return value of __sync_val_compare_and_swap is the old value of the *ptr. To be precise, it's the value which was seen by the implementation of this function once appropriate memory barriers had been put in place. I explicitly state this to cater for the fact that between calling __sync_val_compare_and_swap and executing instructions to enforce the memory barrier the value of *ptr could easily change.

Now, when the function returns what can I do with that return value? There's no point trying to compare it to *ptr because *ptr can now be changed on other threads. Likewise comparing newval and *ptr doesn't really help me either (unless I lock *ptr which probably undermines my use of atomics in the first place).

So all that's really left for me to do is ask whether the return value == oldval, which is effectively (see below for a caveat) asking whether the swap operation took place. So I could have just used __sync_bool_compare_and_swap.

The caveat I just mentioned is that the only subtle difference I can see here is that doing so doesn't tell me whether the swap occured or not, it just tells me that at some point before the memory barrier was released *ptr had the same value as newval. I'm considering the possibility that oldval == newval (although I'd struggle to see a way of implementing the function efficiently so that it could check these values first and not swap if they were the same so it's probably a moot point). However I can't see a situation where knowing this difference would make a difference to me at the call site. In fact, I can't imagine a situation where I would set oldval and newval to be equal.

My question is thus:

Is there any use case in which using __sync_val_compare_and_swap and __sync_bool_compare_and_swap would not be equivalent, i.e. is there a situation where one provides more information than the other?

ASIDE

The reason I was thinking about this was that I found an implementation of __sync_val_compare_and_swap in terms of sync_bool_compare_and_swap which has a race:

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

The race being on the storing of *ptr in ret, as *ptr could change before __sync_bool_compare_and_swap is called. It made me realise that I there doesn't seem to be a safe way (without extra barriers or locks) of implementing __sync_val_compare_and_swap in terms of sync_bool_compare_and_swap. This got me thinking that the former must provide more "information" than the latter, but as per my question I don't see that it really does.

解决方案

The operation provided by __sync_val_compare_and_swap can always be implemented in terms of __sync_bool_compare_and_swap (and of course the other direction is obviously possible), so in terms of power the two are equivalent. However implementing __sync_val_compare_and_swap in terms of __sync_bool_compare_and_swap is not very efficient. It looks something like:

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

The extra work is needed because you could observe failure of __sync_bool_compare_and_swap but then read a new value from *ptr that happens to match oldval.

As for why you might prefer the __sync_val_compare_and_swap behavior, the value that caused failure may give you a starting point to retry the operation more efficiently or might indicate a meaningful cause of the failure for some operation that won't be "retried". As an example, see the code for pthread_spin_trylock in musl libc (for which I am the author):

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

There a_cas is equivalent to __sync_val_compare_and_swap. In some ways this is a stupid example since it's just saving a branch or conditional move by using the old value, but there are other situations where multiple old values are possible and knowing the one that caused the operation to fail matters.

这篇关于__sync_val_compare_and_swap与__sync_bool_compare_and_swap的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆