• Home
  • /
  • Blog
  • /
  • Ongoing Optimization: A Data Race with CppMem

August 18, 2016

But we can improve and further improve the acquire-release semantics of the last post. Why should x be atomic? There is no reason. That was my first but incorrect assumption. See why?

A typical misunderstanding in applying 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 be an atomic variable. So we can further optimize the program.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ongoingOptimizationAcquireReleaseBroken.cpp

#include <atomic>
#include <iostream>
#include <thread>

int x= 0;
std::atomic<int> y{0};

void writing(){  
  x= 2000;  
  y.store(11,std::memory_order_release);
}

void reading(){  
  std::cout << y.load(std::memory_order_acquire) << " ";  
  std::cout << x << std::endl;
}

int main(){
  std::thread thread1(writing);
  std::thread thread2(reading);
  thread1.join();
  thread2.join();
};

 

The program has a data race on x and has, therefore, undefined behavior. 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 simultaneously as the writing of x. So we have concurrent access to a shared variable, one of them is a write. That’s, per definition, a data race. 

The table puts it in a nutshell.

undefinedEng

 

I made this mistake in my presentation “Multithreading done right?” in Berlin. In Moscow, I did it right. I never claimed that the C++ memory model is a piece of cake.

Now it’s time for CppMem. Let’s see what CppMem finds out.

CppMem

 

int main(){
  int x= 0;
  atomic_int y= 0;
  {{{ { 
      x= 2000;
      y.store(11,memory_order_release);
      }
  ||| {
      y.load(memory_order_acquire);
      x;
      }
  }}}
}

The data race occurs if one thread writes x= 2000 and the other reads x. The graph shows a dr symbol (data race) on the arrow.

raceAcquireRelease

What’s next?

The ultimate step in the process of ongoing optimization is still missing. In the next post, I will use the relaxed semantic.

 

 

 

 

 

 

Leave a Reply


Your email address will not be published. Required fields are marked

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

Related Posts