Search code examples
c++multithreadingmutexatomic

Do I always need to protect a variable by mutex / atomic?


Suppose I have lots of threads and a plain, trivially-copyable, non-array (basic types like float, uint16_t etc.) variable called flag. One and only one of the threads often sets the variable's value, while others only read value from it and do not write to it. Do I have to make a variable atomic or guard it by mutex in this case? I know I must protect a variable when multiple threads write to it, but is it necessary to do in my case? Is it platfrom-dependent?


Solution

  • Compilers are free to optimize this:

    while (x != 0) {
      // code it knows does not modify x, nor synchronize with other threads
    }
    

    into

    if (x!=0) {
      while (true) {
        // code it knows does not modify x
      }
    }
    

    ie, check x once and logically assume it cannot be changed.

    If x is atomic, however, they are not allowed to do this, because each read implicitly synchronizes happens-after other threads writes to the variable.

    In general, reading from a variable in thread 1 without synchronization of writing to the same variable in thread 2 is UB under the current C++ memory model.

    This UB can both be a hardware issue, and permission for the compiler to optimize your code in ways you might feel are hostile.

    So yes, you need to tell the compiler that your reads/writes are possibly going to involve inter-thread communication.

    The fun part is that your code might work today, on your hardware with your current compiler. Then a few years from now, an innocuous operating system update, compiler update or hardware revision will make your code fail. What more, the failure case might be rare, and might even happen today!

    You can't prove your code to be correct, because I can prove your code incorrect. You can't prove that the compiler (assuming it doesn't have bugs) will forevermore generate valid assembly that does what you ask, because the standard says your code exhibits UB.

    You might be able to take the assembler output of a particular run of your compiler and prove that under the guarantees that your CPU manufacturer gives for those instructions that the produced code is correct. But I've seen CPU instruction descriptions, and good luck with that.