2

Whilst trying out text book examples of using memory order tags with atomics I realized that even with std::memory_order_relaxed, examples like the one bellow work the same as if stronger memory order tags had been used. On googling I find out its because x86 already guarantees a relatively strongly ordered memory model. My question is when writing atomics based concurrency code on x86 how am I supposed to know if the code I have written is “safe” when even if I mess up the memory order tags it still works fine?

by "safe" i mean will work as intended without the x86 automatic strong memory ordering.

#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x, y;
std::atomic<int> z;
void write_x_then_y()
{
    x.store(true, std::memory_order_relaxed);
    y.store(true, std::memory_order_relaxed);
}
void read_y_then_x()
{
    while (!y.load(std::memory_order_relaxed));
    if (x.load(std::memory_order_relaxed))
        ++z;
}
int main()
{
    x = false;
    y = false;
    z = 0;
    std::thread a(write_x_then_y);
    std::thread b(read_y_then_x);
    a.join();
    b.join();
    assert(z.load() != 0); 
}

On x86 assert will never fire and the above code behaves as if the store to y and the load of y used release/acquire semantics.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Joe Oram
  • 23
  • 3
  • 1
    You could run an instrumenting debugging tool like [TSAN](https://clang.llvm.org/docs/ThreadSanitizer.html), which implements the actual C++ memory model. – Kerrek SB Mar 22 '18 at 22:28
  • Compile-time reordering is still possible regardless of asm-level ordering rules on the target architecture (http://preshing.com/20120625/memory-ordering-at-compile-time/), but if your compiler chooses not to do it, you never see the problem. (And in some cases there's no sane compile-time reordering that can produce the same result as runtime reordering on a weakly-ordered machine like ARM or PowerPC.) – Peter Cordes Mar 23 '18 at 02:02
  • 1
    It's very important (and very hard) to at least try to "prove" that your design is correct for all possible reorderings, as well as testing. Some possible reorderings may only happen under certain conditions that your unit-test or test harness for the whole program doesn't create, or don't happen on the particular hardware you're testing on even if it is weakly-ordered, or could only happen if the compiler had done something differently (so problems are possible with a different compiler, but you can't find them with testing). Still, test on a weakly-ordered C++ implementation if you can! – Peter Cordes Mar 23 '18 at 02:08
  • @PeterCordes I stumbled upon preshing.com about a week ago. I found his articles to be incredibly well written and far better at explaining the details of atomics and memory ordering in C++ than any book. Currently I’m reading through “C++ Concurrency in Action: Practical Multithreading” but its frustrating to read because the guy rambles on too much about simple things, but when something requires explaining he just uses unhelpful and patronizing analogy’s instead of explaining how something works. It’s not bad but it’s not great either. Do you know of any better books to read on the subject? – Joe Oram Mar 23 '18 at 21:09
  • I usually learn stuff from online documentation. Jeff Preshing's articles were what made the pieces of the puzzle click for me, for memory ordering rules. The C++ standard itself is written so abstractly, and not like how hardware works, that it was hard to learn from, but makes some sense once I already understood stuff. I've written some SO answer myself, especially [Can num++ be atomic for 'int num'?]https://stackoverflow.com/questions/39393850/can-num-be-atomic-for-int-num) which goes into detail about what atomicity really is, and how CPUs implement it. (With links for more info.) – Peter Cordes Mar 24 '18 at 01:40
  • https://stackoverflow.com/tags/lock-free/info has links to these. – Peter Cordes Mar 24 '18 at 01:42

0 Answers0