Hi Elad, On Wed, 11 Jan 2023 11:09:14 -0500, Elad Lahav wrote: > Hello all, > > Here's an interesting (hopefully!) question I came across. Consider > the following code: > > int a = 0; > int b = 0; > > void > writer() > { > a = 1; > write_barrier(); > b = 2; > } > > int > reader1() > { > for (;;) { > if (b == 2) { > return a; > } > > yield(); > } > } > > int > reader2() > { > while (b == 2) { > yield(); > } > > return a; > } > > The barrier issued by the writer ensures that if any reader observes b > == 2 then it must also observe a == 1. However, that is only true if > both the compiler and the processor agree that a is loaded after b. > > Consider the two reader functions. These are pretty much the same from > C's point of view, but while the dependency between b and a is clear > in the first case it is less so in the second. I can see a compiler > loading a before the loop in reader2(), but less so in the case of > reader1(). Perhaps that's just the way I interpret the code and a > compiler would treat these as exactly the same. > > Let's assume then that you change the code to do something like return > READ_ONCE(a) in both cases, as the book recommends. Does it change > anything for the compiler? Does it change anything for the processor, > which can reorder the reads? Anecdotally, it seems that a read barrier > helps with reader2(), but I would not expect it to be needed for > reader1(). > > Thoughts? I think Section 15.2.5 "Control Dependencies" can answer your question. TLDR, control dependencies are good for load-to-store ordering. You are talking about load-to-load ordering and control dependencies don't suffice. Thanks, Akira > > --Elad