c++clinux-kernelrace-conditionrcu

Race conditions due to speculative reordering / Linux RCU facility


The following excerpt is taken from the website https://lwn.net/Articles/262464/ and it is dealing with read-inconsistencies of shared data structures (for which there was the RCU created):

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.

It is unclear to me whether the above code is meant to produce erroneous (*) values in the access to p->a etc. which scares the sh.t out of me, or if it is meant as "other CPUs can observe the fetches in another than the written (in source) order" which would be totally,unsurprisingly ok to me.

If the first interpretation is correct I consider systems (compilers) which allow this kind of behaviour broken. My question is if this thing still exists, even when the prevalent architecture (Alpha) already may have vanished.

(*) Erroneous values in the sense of p->a being from one record and p->b from another or something worse

PS: I didn't check but I assume the gp variable to be correctly decorated with atomic or the like.


Solution

  • C++

    That article was written in 2007, so it predates C++11.

    C++11 was the first to define anything about threads in the standard, and with it defines1 what does or doesn't constitute a data race.

    Under the current rules, I believe your conclusion is basically correct--with the quoted code, evaluating p->a, p->b and/or p->c when p==NULL would violate the requirements of the standard, except under the as-if rule (i.e., if the system could assure that evaluating them had no visible effects, even if p was a null pointer).

    C

    The story here is pretty much the same--the C11 standard was the first to define threading and how sequencing works when you use them. So, when the article was written, the standard didn't have any rules about it. Although they don't use the same terminology, the C and C++ committees have coordinated on this, so I believe they at least intend for the rules in the two languages to be the same in this regard.


    1. Well, it tries to, anyway--if memory serves, a few holes have been found in how things were defined in the 2011 standard, so there have been updates in the newer standards. But the main point is that before the 2011 standard, it didn't even try.