Debugging & Tooling

ThreadSanitizer (TSan)

/ TSan -> TEE-san /

When two threads share data and at least one of them writes it without proper synchronization, you have a data race - and these are among the cruelest bugs in programming. They show up only under particular timing, often once in thousands of runs, often vanish when you add a print or attach a debugger, and can corrupt data silently. You need a tool that understands the rules of safe sharing and flags a race even on a run where, by luck, nothing visibly broke. ThreadSanitizer is that tool.

Concretely, TSan is a sanitizer in gcc and clang enabled with -fsanitize=thread (plus -g). The compiler instruments every memory access and every synchronization operation (locks, unlocks, atomics, thread creation and join). At run time TSan tracks a 'happens-before' relationship: which accesses are guaranteed ordered by synchronization and which are not. When it sees two threads access the same memory location, at least one access being a write, with no synchronization ordering them, it reports a data race - even if on this particular run the two accesses happened not to overlap. The report names both accesses with stack traces, the threads involved, and where the memory was allocated. The cost is heavier than ASan - roughly 5 to 15 times slower and a lot more memory - and TSan cannot be combined with ASan.

Why it matters: data races are nondeterministic and famously hard to reproduce, so finding them by ordinary debugging is nearly hopeless; TSan finds them by analyzing the synchronization rather than by relying on the bug to actually manifest. Honest caveats: it only detects races on memory actually touched during the run and only on threads it instrumented, so untested code paths and uninstrumented libraries hide races; it can occasionally miss or, more rarely, mis-flag with custom synchronization it does not understand; and being a separate axis from memory errors, you run TSan builds for concurrency and ASan builds for corruption, not both at once. A clean TSan run is strong evidence, not a proof, of race-freedom.

$ gcc -g -fsanitize=thread race.c -o a -lpthread on two threads doing counter++ with no mutex prints 'WARNING: ThreadSanitizer: data race ... Write of size 4 by thread T2 ... Previous write ... by thread T1', pinpointing both unsynchronized accesses - even when the printed counter looked correct.

TSan reports a data race on counter++ even on a run that produced the right number.

TSan detects a race from the synchronization rules, so it flags one even when this run happened to produce a correct result - that is its whole point. It only covers memory and threads exercised during the run, cannot be combined with AddressSanitizer, and may be confused by hand-rolled synchronization it does not recognize.

Also called
TSan-fsanitize=threaddata-race detector