Hello, I am writing a wrapper for a std::mutex (called ObservableMutex) that collects stats on various things including the number of contentions on the mutex. As part of that implementation, I have some code that behaves like the following:
void ObservableMutex::lock() {
if (!_mutex.try_lock()) {
_contentions.fetchAndAddRelaxed(1);
_mutex.lock();
}
}
When using this mutex wrapper, I see what looks like TSAN false negatives. Consider this test which demonstrates a simple lock order inversion:
TEST(ObservableMutexTsanTest, LockOrderInversion) {
ObservableMutex<std::mutex> m1;
ObservableMutex<std::mutex> m2;
std::thread t1([&] {
std::lock_guard lg(m1);
std::lock_guard lg2(m2);
});
t1.join();
std::thread t2([&] {
std::lock_guard lg(m2);
std::lock_guard lg2(m1);
});
t2.join();
}
When I use the ObservableMutex, the test passes with no TSAN errors. However, if I switch the ObservableMutex to just a plain std::mutex, TSAN correctly reports a lock order inversion error. Also, deleting the if condition in ObservableMutex::lock and unconditionally calling into the body causes TSAN to identify the error.
So a try_lock check guarding a call to lock appears to trick TSAN into outputting a false negative. Is this issue known, and if not is this something that the TSAN team could investigate/fix?
For reference, I am using TSAN from llvm 19.1.7
Thanks,
Joseph Prince