1

This is a follow-up to Volatile in C++11

In the question, I was told that the following code is exhibiting undefined behavior in C++11, which confused me greatly. Is there any material that I can read (sections of the standard perhaps) concerning the behavior of volatile in C++11?

Or can someone please explain where the problem lies?

#include <iostream>
#include <chrono>
#include <thread>
using namespace std;

volatile int notify;

void watcher()
{
    this_thread::sleep_for(chrono::seconds(2));
    notify = 1;
    cout << "Notification sent." << endl;
}

int main()
{
    thread(watcher).detach();

    notify = 0;
    while (!notify)
    {
        cout << "Waiting." << endl;
        this_thread::sleep_for(chrono::seconds(1));
    }

    cout << "Notification received." << endl;

    return 0;
}
Community
  • 1
  • 1
Šimon Tóth
  • 35,456
  • 20
  • 106
  • 151

1 Answers1

7

The standard describes the memory model, and in particular the notion of a "data race" (Section 1.10). It is fairly obvious that your code has a data race for the variable notify, and thus undefined behaviour.

To fix the problem, either guard access to notify by a lock, or make it an atomic variable such as an std::atomic<int>.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Awesome I finally managed to find the specific part of the standard: _"Two expression evaluations conflict if one of them modifies a memory location and the other one accesses or modifies the same memory location."_ and _"The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior."_ – Šimon Tóth Oct 14 '12 at 02:47
  • Wow. That's a very, very strict rule. – Šimon Tóth Oct 14 '12 at 02:51
  • Hmm, I'm kind of wondering how you would write a threading library with this rule in place. Basically forces you to rely on compiler ABI. Which I guess you have to do anyway... – Šimon Tóth Oct 14 '12 at 02:56
  • 2
    @Let_Me_Be: Well, you could just use atomic variables to construct a locking primitive, for example. – Kerrek SB Oct 14 '12 at 03:00
  • Hang on a second... is there a sane compiler/implementation where the value of notify is anything other than 0 or 1? My gut reaction to this code was "this is fine... not the fastest way to notify but it'll work". In fact, shouldn't it be fine even without volatile? – mark Oct 15 '12 at 12:33