-
Notifications
You must be signed in to change notification settings - Fork 15.6k
Description
Hi TSAN team,
We're developing a pure-Go race detector (racedetector) and during comparative testing discovered behavioral differences that may indicate potential false negatives in TSAN's Go integration.
Observed Behavior
When testing concurrent Go programs, our detector reports races that TSAN misses. The discrepancy is reproducible and GOMAXPROCS-dependent.
Test case:
package main
import "sync"
//go:noinline
func update(ptr *int) {
*ptr++
}
func main() {
var shared int
var wg sync.WaitGroup
for range 2 {
wg.Add(1)
go func() {
defer wg.Done()
for range 100 {
update(&shared)
}
}()
}
wg.Wait()
}- Our detector: Reports race on
sharedvariable - Go's -race (TSAN): No race reported
Potential Root Causes (from source analysis)
We analyzed compiler-rt/lib/tsan and identified several mechanisms that may contribute:
1. Shadow Memory Eviction (tsan_defs.h:58)
constexpr uptr kShadowCnt = 4; // 4 shadow cells per 8 bytesUnder high contention, older access records may be evicted before a race is detected.
2. Thread Slot Limit (tsan_defs.h:57)
constexpr uptr kThreadSlotCount = 256;Programs with >256 goroutines experience slot recycling, potentially losing happens-before information.
3. Conditional Fork Happens-Before (tsan_rtl_thread.cpp:140-145)
if (!thr->ignore_sync) {
thr->clock.ReleaseStore(&arg.sync);
}When ignore_sync is active, child threads may not inherit parent's vector clock.
4. Go Runtime ignore_sync (tsan_go.cpp:279-283)
void __tsan_go_ignore_sync_begin(ThreadState *thr) { ... }
void __tsan_go_ignore_sync_end(ThreadState *thr) { ... }Go runtime can disable synchronization tracking for internal operations, but this may inadvertently affect user-code race detection.
Questions
- Are these known limitations documented somewhere?
- Is the
ignore_syncbehavior during goroutine creation intentional? - Would increasing
kShadowCntorkThreadSlotCountbe beneficial for Go workloads? - Would you be interested in reviewing our implementation for comparison?
Our Implementation Differences
| Aspect | TSAN | Our Detector |
|---|---|---|
| Thread slots | 256 | 65,536 |
| Shadow cells | 4 per 8 bytes | 1 per address (CAS-based) |
| Fork HB | Conditional | Unconditional |
| ignore_sync | Yes | No |
References
- Our detector: https://github.com/kolkov/racedetector
- Related Go issue: runtime/race: eliminate dependency on cmd/cgo golang/go#6508
- FastTrack paper: Flanagan & Freund, PLDI 2009
We're not claiming TSAN has bugs — these may be intentional performance tradeoffs. We'd appreciate any guidance on expected behavior.
Thank you for TSAN — it's an invaluable tool that inspired our work.