| Issue |
173049
|
| Summary |
Question: Potential false negatives in Go race detection due to shadow eviction and thread slot recycling
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
kolkov
|
Hi TSAN team,
We're developing a pure-Go race detector ([racedetector](https://github.com/kolkov/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:**
```go
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 `shared` variable
- **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`)
```cpp
constexpr uptr kShadowCnt = 4; // 4 shadow cells per 8 bytes
```
Under high contention, older access records may be evicted before a race is detected.
### 2. Thread Slot Limit (`tsan_defs.h:57`)
```cpp
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`)
```cpp
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`)
```cpp
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
1. Are these known limitations documented somewhere?
2. Is the `ignore_sync` behavior during goroutine creation intentional?
3. Would increasing `kShadowCnt` or `kThreadSlotCount` be beneficial for Go workloads?
4. 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: https://github.com/golang/go/issues/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.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs