Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guarantees of benign race conditions in C++

I know the C++ standard doesn't guarantee anything in presence of a data race (I believe a data race has undefined behavior, meaning anything goes, including program termination, modifying random memory, etc...).

Is there any architecture where a data race that consists of one thread writing to a memory location and one thread reading from the same location (without synchronization) doesnt result in the read operation reading an undefined value and where the memory location is "ultimately" (after a memory barrier) updated to the value that was written by the write operation?

[edited to replace "race condition" with "data race"]

like image 727
anonymous Avatar asked Dec 14 '25 13:12

anonymous


1 Answers

The problem with data races is not, that you can read a wrong value on a machine level. The problem with data races is, that both compiler and processor perform a lot of optimizations on the code. To make sure that these optimizations are correct in the presence of multiple threads, they need additional information about variables that can be shared between threads. Such optimizations can for example:

  • reorder operations
  • add additional load and store operations
  • remove load and store operations

There is a good paper benign data races by Hans Boehm called How to miscompile programs with "benign" data races. The following excerpt is taken from this paper:

Double checks for lazy initialization

This is well-known to be incorrect at the source-code level. A typical use case looks something like

if (!init_flag) {
    lock();
    if (!init_flag) {
        my_data = ...;
        init_flag = true;
    }
    unlock();
}
tmp = my_data;

Nothing prevents an optimizing compiler from either reordering the setting of my_data with that of init_flag, or even from advancing the load of my_data to before the first test of init_flag, reloading it in the conditional if init_flag was not set. Some non-x86 hardware can perform similar reorderings even if the compiler performs no transformation. Either of these can result in the final read of my_data seeing an uninitialized value and producing incorrect results.


Here is another example, where int x is a shared and int r is a local variable.

int r = x;
if (r == 0)
    printf("foo\n");
if (r != 0)
    printf("bar\n");

If we would only say, that reading x leads to an undefined value, then the program would either print "foo" or "bar". But if the compiler transform the code as follows, the program might also print both strings or none of them.

if (x == 0)
    printf("foo\n");
if (x != 0)
    printf("bar\n");
like image 75
nosid Avatar answered Dec 16 '25 01:12

nosid



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!