反转CAS,其中只有在新值未更改的情况下才进行存储.有什么方法可以在原子上实现这一点吗



基本上,我要寻找的是能够原子地完成以下逻辑的东西。

#define FAILED 1
#define SUCCESS 0
int foo (uint64_t * src, uint64_t * dst, uint64_t expected) {
if (*src == expected) {
*dst = *src;
return SUCCESS;
}
return FAILURE;
}

对于这个特定的用例,在所有情况下expected == 0*dst都不能被任何其他线程修改。然而,*src可以由其他线程同时修改(这些其他线程可以在另一个内核上,否则我可以使用可重启序列(

如果*src != expected(带有expected == 0(,则不能修改*dst

我有不变的*src != *dst,除非是*src == *dst == 0(这可能有助于找到解决方案(。

最后,如果它能够实现任何潜在的解决方案,我可以保证*src*dst要么在同一个缓存线上,要么在不同的缓存线上。

我不相信任何内置的原子操作(https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html)可以实现这一点,所以我认为唯一的方法是使用lock前缀的一些内联程序集,或者使用某些函数的一些副作用(即CAS将在失败时设置预期的事实(。

总结一下,我正在寻找一种用内联asm实现自定义原子操作的方法,或者我可以使用bultin原子来实现这一点的某种方法。

谢谢!

我不明白你想要这个原语做什么,我认为把它称为"原子";。如果在任何时刻观察到*src具有expected的值,则发生对*dst的存储是有效的。任何其他线程都无法观察到该操作的原子性或缺乏原子性。因此,按照您编写的方式编写它(尽管*src具有适当的原子类型(应该没问题。

如果你不同意这种推理,请解释一下你想在其中使用原语的更大范围,因为它几乎肯定是错误的,而真正的问题是理解它是如何错误的,并找出适合它的东西。(解决方案几乎肯定是停止尝试使用原子论并使用锁。(

一个普通的CAS只有一个内存操作数!这就像是一个部分DCAS,也就是CAS2。

如果将src和dst彼此相邻,则可以使用宽CAS(如lock cmpxchg16b(来模拟它,但如果dst发生变化,则可能会错误地失败。

或者,如果您的CPU未通过微码更新禁用,则可以使用Intel TSX事务内存。(我认为,由于某种MDS漏洞的原因,HLE在微码中被禁用,但AFAIK RTM仍然可以在CPU上使用,因为正确性的原因,即在Skylake上工作,稍后至少在一些Broadwell上工作。(


请注意,整个缓存线的AVX-512负载并不能保证是原子的,但在所有当前的AVX-512CPU上,人们认为它们在实践中是原子的。不幸的是,即使16字节SIMD加载/存储原子性很普遍,供应商也不愿意提供任何CPUID方法来检测大于8字节的原子性保证。注意,内核之间的互连可能会导致撕裂,而不仅仅是狭窄的加载/存储执行单元:SSE指令:哪些CPU可以执行原子16B内存操作?显示了由于HyperTransport,K10 Opteron上不同套接字中的内核之间仅的8字节边界撕裂。

最新更新