由于推测性重新排序 / Linux RCU 设施导致的竞争条件

Race conditions due to speculative reordering / Linux RCU facility

以下摘录自网站 https://lwn.net/Articles/262464/,它处理共享数据结构(为此创建了 RCU)的读取不一致:

p = gp;
if (p != NULL) {
  do_something_with(p->a, p->b, p->c);
}

Although this code fragment might well seem immune to misordering, unfortunately, the DEC Alpha CPU [PDF] and value-speculation compiler optimizations can, believe it or not, cause the values of p->a, p->b, and p->c to be fetched before the value of p! This is perhaps easiest to see in the case of value-speculation compiler optimizations, where the compiler guesses the value of p, fetches p->a, p->b, and p->c, then fetches the actual value of p in order to check whether its guess was correct. This sort of optimization is quite aggressive, perhaps insanely so, but does actually occur in the context of profile-driven optimization.

我不清楚上面的代码是否意味着在访问 p->a 等时产生错误的 (*) 值,这让我害怕 sh.t,或者如果它是意思是 “其他 CPU 可以观察到不同于书面(源)顺序的提取” 这对我来说完全可以,不足为奇。

如果第一种解释是正确的,我会考虑允许这种行为被破坏的系统(编译器)。我的问题是这个东西是否仍然存在,即使流行的架构(Alpha)可能已经消失了。

(*) 错误值 p->a 来自一条记录,而 p->b 来自另一条或更糟的

PS:我没有检查,但我假设 gp 变量被正确修饰为 atomic 或类似的。

C++

那篇文章写于 2007 年,所以它早于 C++11。

C++11 是第一个在标准中定义有关线程的任何内容的人,它定义了1什么构成或不构成数据竞争。

在目前的规则下,我相信你的结论基本上是正确的--引用代码,评估p->ap->b and/or p->cp==NULL 将违反标准的要求,除非在 as-if 规则下(即,如果系统可以确保评估它们没有可见效果,即使 p 是空指针)。

C

这里的故事几乎是一样的——C11 标准是第一个定义线程以及使用它们时顺序如何工作的标准。所以,在写这篇文章的时候,标准并没有关于它的任何规则。尽管他们不使用相同的术语,但 C 和 C++ 委员会已经就此进行了协调,因此我相信他们至少打算在这方面使两种语言的规则相同。


1. 好吧,无论如何它都会尝试——如果没记错的话,在 2011 年标准中的定义方式中发现了一些漏洞,因此在较新的标准中进行了更新。但是重点是在2011年的标准之前,连试都没试过。