Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where do I set memory barriers so that conditional loops observe multithread value changes?

Assuming a thread running indefinitely until it's member variable stop is set. Like

while (!stop) {
  // do something
}

I want to prevent it turning into this

if (!stop) {
  while (true) {
  
  }
}

So where would I set memory barriers / fences so that this optimization can't be performed?
Also, would such a fence already be enough to ensure a change to true from another thread is visible in the next iteration?

like image 791
Regyn Avatar asked Mar 02 '23 11:03

Regyn


1 Answers

So where would I set memory barriers / fences so that this optimization can't be performed?

A memory barrier/fence restricts the memory ordering, which prevents the compiler from reordering two or more memory accesses in the same thread. However, memory ordering is not the issue here, as you are only talking about a single memory access per thread.

Instead, you are talking about two memory operations on the same variable from two different threads. Therefore, this is a matter of thread synchronization, not memory ordering.

According to §6.9.2.2 ¶21 of the ISO C++20 standard, if two threads access the same variable without a synchronization operation (e.g. mutex) in between, and if these memory accesses are not

  1. both atomic, or
  2. both read-only,

then this results in undefined behavior.

Therefore, in the case of your while loop

while (!stop) {
  // do something
}

assuming that stop is a non-atomic data type, the compiler is allowed to assume that the variable stop will never be modified by another thread while the loop is running (unless you happen to have a synchronization operation inside the loop), because doing so would result in undefined behavior (which means that the compiler would be allowed to do anything).

For this reason, a good optimizing compiler will effectively change the code to the following:

if (!stop) {
  while (true) {  
    // do something
  }
}

However, if you change the variable stop to an atomic data type, such as std::atomic_flag or std::atomic<bool>, then it is not undefined behavior for several threads to access this variable at the same time. In that case, the compiler will not be allowed to assume that stop will not be modified by another thread, so that it will not be allowed to perform the optimization mentioned above.

like image 93
Andreas Wenzel Avatar answered Mar 04 '23 04:03

Andreas Wenzel