0

Consider the following code:

#include <atomic>
#include <iostream>
#include <thread>

std::atomic<int> x, y;
std::atomic<int> r1, r2;

void f()
{
  r1 = y.load();
  x = r1.load();
}

void g()
{
  r2 = x.load();
  y = 42;
}

int main()
{
  x = 0;
  y = 0;
  r1 = 0;
  r2 = 0;

  std::thread t1(f);
  std::thread t2(g);

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

  std::cout << r1 << " " << r2 << std::endl;
}
  1. If I compile this code with compilers/linux-x86_64-2.10.1/gnu7.1.0/bin/g++ -fsanitize=thread -O3 -std=c++11 main.cpp -o a.out, TSan does not provide any warnings and/or threading errors.
  2. However, this code is allowed to produce both 42 0 and 0 0 as output.
    • If g() is executed before f() starts, then r1 = y.load() will have a value of 42
    • If g() is not executed before f() starts, then r1 = y.load() will have a value of 0.
  3. Is this something that I should be expecting TSan to catch, or are my expectations completely wrong here?
    • If my expectations are wrong, what can be done (other than code inspection, which can be very difficult for larger code bases) to find bugs such as this?
    • In the event that there should be some error thrown, is there some specific option that I am perhaps missing (I'm using all defaults as specified in the document here)?
R_Kapp
  • 2,818
  • 1
  • 18
  • 32
  • Which is you wanted output ? – Jarod42 Mar 23 '18 at 12:58
  • @Jarod42: For this particular case, I don't particularly care for a specific output, I just want `Tsan` (a third-party library provided as part of Clang and is back-ported to GCC) to provide an error message indicating a potential threading issue. – R_Kapp Mar 23 '18 at 13:00
  • Open question: `std::atomic` ensures reading/writing the `T` is atomic. Is the compiler allowed to reorder the statements of `f()` and `g()`, since they're not depending on eachother? (all variables being `42`) – Caramiriel Mar 23 '18 at 13:01
  • From https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual: *"`ThreadSanitizer` (aka TSan) is a data race detector for C/C++."* There is no data races here, in no way can one of your variable be access by the two threads at the same time. If you want to ensure order of operations, use `std::memory_order`. – Holt Mar 23 '18 at 13:02

1 Answers1

4

From clang's documentation

ThreadSanitizer is a tool that detects data races

You don't have a data race since all of your variables are atomic, so there is nothing to report.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • There is, however, a race condition. I thought `TSan` claimed to catch those as well - you are saying that it does not? – R_Kapp Mar 23 '18 at 13:02
  • 1
    @Holt: That is a _data race_, not a _race condition_. The two are different, see, e.g., [here](https://stackoverflow.com/questions/11276259/are-data-races-and-race-condition-actually-the-same-thing-in-context-of-conc) – R_Kapp Mar 23 '18 at 13:04
  • @R_Kapp There is no data race here. It is not possible to read or write to any of your variables while the same thing happens in the other thread. All reads and writes are ordered, just the order is unspecified. – NathanOliver Mar 23 '18 at 13:04
  • "just the order is unspecified" That is the definition of a race condition - see my other comment – R_Kapp Mar 23 '18 at 13:05
  • 2
    @R_Kapp The tool only detects data races, not race conditions. – NathanOliver Mar 23 '18 at 13:05
  • 1
    @R_Kapp ThreadSanitizer only detects data races, not generic race conditions. You are correct the two are different, but you are also mistaken of the scope of what ThreadSanitizer does. – Alex Huszagh Mar 23 '18 at 13:05
  • 1
    @AlexanderHuszagh: Fair enough. I don't know where I got the idea that it did race conditions as well then... Probably just wishful thinking. – R_Kapp Mar 23 '18 at 13:06
  • 1
    @R_Kapp This is not only for C++. If you are familiar with golang, it's the same https://golang.org/doc/articles/race_detector.html – llllllllll Mar 23 '18 at 13:07
  • 3
    Just a little clarification: the C++ language definition uses the term "data race"; it means that two or more threads can access the same region of storage simultaneously and at least one of them modifies it. `std::atomic` prevents simultaneous access, so there is no "data race" as the standard defines it. That doesn't guarantee that the behavior of a program is completely determined. – Pete Becker Mar 23 '18 at 13:08