TSAN False Negative With try_lock

22 views
Skip to first unread message

Joseph Prince

unread,
Mar 2, 2026, 4:39:31 AMMar 2
to thread-sanitizer

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

Dmitry Vyukov

unread,
Mar 2, 2026, 4:46:34 AMMar 2
to Joseph Prince, thread-sanitizer, ccot...@bloomberg.net
Hi Joseph,

Yes, this is a known limitation. TSAN is a dynamic tool and only
observes and detects bugs that happen in the concrete execution. There
are also tons of false nehative race reports for this reason. It's not
static analysis.

You either need a stress test that actually exposes the inversion, or
perhaps Chris' new delay injection feature may help to trigger such
bugs:
https://github.com/llvm/llvm-project/pull/178836
(it's only in newer clang versions, and require manual enabling with
TSAN_OPTIONS).

Chris Cotter (BLOOMBERG/ 731 LEX)

unread,
Mar 2, 2026, 4:51:13 PMMar 2
to dvy...@google.com, joseph...@mongodb.com, thread-s...@googlegroups.com
Hi - I ran the code below with a trunk build of clang, and with TSAN_OPTIONS=enable_adaptive_delay=1, the mutex cycle is detected more frequently, though I don't have exact measurements. Maybe twice as much more frequently?

I ran the test below in a loop 100 times, and added a small watchdog thread to kill the program if it detected the test program truly became stuck in a deadlock. https://godbolt.org/z/vdzK5qn68 - you can't really re-run the test without changing a line of code to trigger compiler explorer to re-compile and re-run the code (at least, I'm not sure how to do that in compiler explorer).

I believe the new adaptive delay will be available in clang-23, released in the second half of the this year if I understand the release cycle correctly.

-chris
Reply all
Reply to author
Forward
0 new messages