Home > Back-end >  Is it a data race?
Is it a data race?

Time:04-17

volatile global x = 0;
reader() {
  while (x == 0) {}
  print ("World\n");
}
writer() {
  print ("Hello, ")
  x = 1;
}
thread (reader);
thread (writer);

https://en.wikipedia.org/wiki/Race_condition#:~:text=Data race[edit,only atomic operations.

From wikipedia,

The precise definition of data race is specific to the formal concurrency model being used, but typically it refers to a situation where a memory operation in one thread could potentially attempt to access a memory location at the same time that a memory operation in another thread is writing to that memory location, in a context where this is dangerous.

  1. There are at least one thread that writes to x. (writer)
  2. There are at least one thread that reads to x. (reader)
  3. There is not any synchronization mechanism for accessing x. (Both of two threads access x without any locks.)

Therefore, I think the code above is data race. (Obviously not a race condition) Am i right?

Then what is the meaning of data race when a code is data race, but it generates the expected output? (We will see "Hello, World\n", assuming processor guarantees that a store to an address becomes visible for all load instructions issued after the store instruction)

----------- added working cpp code ------------

#include <iostream>
#include <thread>

volatile int x = 0;

void reader() {
    while (x == 0 ) {}
    std::cout << "World" << std::endl;
}

void writer() {
    std::cout << "Hello, ";
    x = 1;
}

int main() {
    std::thread t1(reader);
    std::thread t2(writer);
    t2.join();
    t1.join();
    return 0;
}

CodePudding user response:

Yes, data race and consequently undefined behavior in C . Undefined behavior means that you have no guarantee how the program will behave. Seeing the "expected" output is one possible output, but you are not guaranteed that it will happen.

Here x is non-atomic and is read by thread t1 and written by thread t2 without any synchronization and therefore they cause a data race.

volatile has no impact on whether or not an access is a data race. Only using an atomic (e.g. std::atomic<int>) can remove the data race.

That said, on many common platforms writing to a int will be atomic on the hardware level, the compiler will not optimize away volatile accesses and will probably also not reorder volatile accesses with IO and therefore it will probably happen to work on these platforms. The language doesn't make this guarantee though.

CodePudding user response:

Yes, this is a data race and UB.

[intro.races]/2

Two expression evaluations conflict if one of them modifies a memory location ... and the other one reads or modifies the same memory location.

[intro.races]/21

Two actions are potentially concurrent if:
— they are performed by different threads, ...

...

The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other, ...

Any such data race results in undefined behavior.

For two things in different threads to "happen before" one another, a synchronization mechanism must be involved, such as non-relaxed atomics, mutexes, and so on.

  • Related