2015-04-28 81 views
3

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

首先,让我们有参考函数签名(从GCC文档减去VAR参数):

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中,因为在调用__sync_bool_compare_and_swap之前* ptr可能会改变。它让我意识到,在sync_bool_compare_and_swap方面实现__sync_val_compare_and_swap似乎并不是一种安全的方式(没有额外的障碍或锁定)。这让我想到前者必须提供比后者更多的“信息”,但根据我的问题,我没有看到它确实如此。

+0

一种用法:返回的当前值可用于重试。我知道有原子增量,但为了说明目的,假设你想实现这个。您可以简单地使用oldvalue作为前增量和新值作为前增量来调用val_compare_and_swap。如果成功,你就完成了。如果不是,则可以通过递增返回的值并再次调用val_compare_and_swap来重试。使用bool版本,您需要额外读取当前的重试值。 – kaylum

+0

你是对的,你的“旁边”的代码是越野车/有严重的竞争条件。需要重试循环才能使其正确。看到我的答案。 –

回答

3

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

+0

我不确定我是否清楚__sync_val_compare_and_swap的实现。你为什么要循环?我对__sync_bool_compare_and_swap的理解是,它只会尝试只交换一次,而“failure”意味着oldval!= * ptr,而不是函数以某种方式失败。至于类型为tmp = * ptr的行;起初,它看起来像是有一场比赛,但我会先等待你的回答,因为它可能会澄清一些事情。感谢您的答案中的“为什么”部分。 –

+0

@AndrewParker:因为要提供'__sync_val_compare_and_swap'的工作实现,您必须**返回一个值,指向对象在操作过程中有一段时间不等于参数'oldval'。找到这样的值是不可能的,除非你找到它。设想另一个并发线程快速切换(通过有效的原子交换)对象的'oldval'和其他隐藏值之间的值。 –

+0

我明白了。最后一件事麻烦了我。 * ptr可以在您将其与oldval进行比较之前更改,但它可能会更改为与oldval不相等的另一个值。因此,这个实现可能会返回一个与交换失败原因无关的值。这是一个迂腐点,但我不知道它是否与功能的规格相矛盾。引用GCC页面:“在操作前,”val“版本返回* ptr的内容。”这个实现不会在* op之前返回* ptr **的内容。 –

相关问题