1
#include <iostream>
#include <thread>


int x = 0;
int y = 0;

void f()
{
    std::cout <<"f called\n";

    static int c = 0;
    while(y == 0)
    {
        ++c;
    }
    std::cout << "c=" << c << std::endl;
    std::cout << "x=" << x << std::endl;
}

void g()
{
    std::cout <<"g called\n";
    x = 42;
    y = 1;
}

int main()
{
    std::thread t1(f);
    std::thread t2(g);

    t1.join();
    t2.join();

    return 0;
}

f is supposed to print 'x=42' when the flag y is set from the other thread (well, it cal also print x=0, but that's not the issue here)

when running in debug mode, it works as expected:

f called
g called
c=80213
x=42

but in release mode, the second thread seems freezing and the program never end:

f called
g called

could someone please explain why?

PS. The program was compiled with mignw g++ 4.8.0

user327843
  • 482
  • 6
  • 17
  • 3
    Data race, undefined behaviour, etc etc. A.k.a. "multithreading is really hard, even your hello world will be wrong." – Kerrek SB Aug 22 '13 at 08:33
  • @Kerrek SB, yes, this is definitely a data race condition, but my question is, why the first thread keeps running even when the value in 'y' changed from 0 ? – user327843 Aug 22 '13 at 08:40
  • 2
    It's "undefined behaviour": Anything could happen. And indeed, anything did happen. So everything is as expected. – Kerrek SB Aug 22 '13 at 08:41
  • Most likely, the compiler sees that the while loop cannot possibly terminate, because `y` is not atomic and there's no synchronization in the loop, and thus any modification to `y` would be undefined, so it replaces the whole loop by an infinite loop. – Sebastian Redl Aug 22 '13 at 08:44
  • @Sebastian Redl, not sure about that because the issue disappear when I add an std::cout inside the loop.. while(y <= 0) { ++c; std::cout << ""; } – user327843 Aug 22 '13 at 08:47
  • @user327843: Undefined behavior means ***undefined behavior***. Anything can happen, including it working sometimes when seemingly unimportant changes are made. – Nicol Bolas Aug 22 '13 at 08:49
  • @user327843 How would the compiler know that the output operation doesn't change `y` somewhere? It's unlikely it analyzes the entire code. Also, the operation most likely contains synchronization statements, which would be enough to make the compiler abandon its reasoning about whether `y` can be modified by other threads or not. – Sebastian Redl Aug 22 '13 at 11:55

1 Answers1

6

The C++11 threaded memory model does not require that code in one thread will see memory changes caused by code in another thread unless:

  1. The two threads synchronize their access to the memory via the use of std::mutex. That is, the receiving thread must wait on a mutex held by the writing thread. And the writing thread must have written the data before releasing that mutex.
  2. The memory in question is governed by std::atomic, and the appropriate atomic memory accesses are used to write to and read from it.

If neither of these things is in play, then any attempt by one thread to read from memory that is potentially modified by another thread is considered a "data race". And therefore results in undefined behavior.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Is this a case where making `y` `volatile` could help/resolve the problem? – RedX Aug 22 '13 at 08:46
  • 2
    @RedX: [No.](http://stackoverflow.com/questions/12878344/volatile-in-c11/12878500#12878500) – Nicol Bolas Aug 22 '13 at 08:47
  • @RedX, Yes. I can confirm that making y volatile resolved the issue. – user327843 Aug 22 '13 at 08:49
  • 3
    @user327843: Undefined behavior means ***undefined behavior***. Just because your code *works* doesn't mean that the C++ standard ***guarantees*** that it will. And as far as the C++ standard is concerned, `volatile` has jack squat to do with the visibility of memory changes across threads. – Nicol Bolas Aug 22 '13 at 08:50
  • @user327843 You should definitely read the link posted by Bolas. – RedX Aug 22 '13 at 08:52