Thread sanitizer gives false negative for "function race"
Asked Answered
S

1

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)?
Shaunna answered 23/3, 2018 at 12:53 Comment(4)
Which is you wanted output ?Flap
@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.Shaunna
Open question: std::atomic<T> 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)Solmization
From 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.Tract
E
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.

Edith answered 23/3, 2018 at 12:59 Comment(9)
There is, however, a race condition. I thought TSan claimed to catch those as well - you are saying that it does not?Shaunna
@Holt: That is a data race, not a race condition. The two are different, see, e.g., hereShaunna
@Shaunna 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.Edith
"just the order is unspecified" That is the definition of a race condition - see my other commentShaunna
@Shaunna The tool only detects data races, not race conditions.Edith
@Shaunna 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.Delamination
@AlexanderHuszagh: Fair enough. I don't know where I got the idea that it did race conditions as well then... Probably just wishful thinking.Shaunna
@Shaunna This is not only for C++. If you are familiar with golang, it's the same golang.org/doc/articles/race_detector.htmlEffluent
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.Newell

© 2022 - 2024 — McMap. All rights reserved.