But we can do better and further improve the acquire-release semantic of the last post. Why should x be an atomic? There is no reason. That was my first, but incorrect assumption. See why?
A typical misunderstanding in the application of the acquire-release semantic is, to assume, that the acquire operation is waiting for the release operation. So based on this assumption you may think, that x has not to be an atomic variable. So we can further optimize the program.
int x= 0;
std::cout << y.load(std::memory_order_acquire) << " ";
std::cout << x << std::endl;
The program has a data race on x and has therefore undefined behaviour. If y.store(11,std::memory_order_release) (line 12) is executed before y.load(std::memory_order_acquire) (line 16), the acquire-release semantic guarantees, that x= 2000 (line 11) is executed before the reading of x in line 17. But if not. In this case, the reading of x will be executed at the same time as the writing of x. So we have concurrent access on a shared variable, one of them is a write. That's per definition a data race.
The table puts it in a nutshell.
I made this mistake in my presentation "Mulithreading done right?" in Berlin. In Moscow, I did it right. I never claimed, that the C++ memory model is a piece of cake.
Now its time for CppMem. Let's see, what CppMem finds out.
int x= 0;
atomic_int y= 0;
The data race occurs, if one thread is writing x= 2000 and the other thread is reading x. In the graph, we get a dr symbol (data race) on the arrow.
The ultimate step in the process of ongoing optimization is still missing. In the next post, I will use the relaxed semantic.
Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library". Get your e-book. Support my blog.