-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Problem
racedetector v0.8.4 reports ~4,600 false positive DATA RACE warnings on a project with 24 test suites and 1,500+ tests. The standard Go race detector (go test -race with Go 1.25.6) reports zero races on the same codebase.
The false positives fall into three categories:
1. Heap address reuse between sequential test goroutines
Tests run sequentially (no t.Parallel()). Each test allocates a struct via &App{...}. After test N completes, GC collects the struct. Test N+1 allocates a new struct at the same heap address. The racedetector flags the two writes from different tRunner goroutines as a race.
=== RUN TestNewApp_WithPlatformProvider
==================
WARNING: DATA RACE
Write at 0x000000c000079eb8 by goroutine 4:
app.New()
app.go:90 ← a := &App{theme: t}
app.TestNewApp_WithPlatformProvider()
testing.tRunner()
[epoch: 2@4]
Previous Write at 0x000000c000079eb8 by goroutine 3:
app.New()
app.go:90 ← same line, DIFFERENT test
[epoch: 2@3]
==================
--- PASS: TestNewApp_WithPlatformProvider (0.00s)
There is no actual race — goroutine 3's test completed before goroutine 4's test started. The testing.tRunner synchronization (goroutine join → next goroutine fork) establishes a happens-before relationship that the racedetector doesn't track.
The standard Go race detector (ThreadSanitizer) handles this correctly because it clears shadow memory state on runtime.mallocgc.
2. sync.Mutex/RWMutex happens-before not tracked
The racedetector flags accesses that are properly protected by sync.RWMutex:
// layout/registry.go — properly synchronized
func (r *Registry) List() []string {
r.mu.RLock() // ← Lock acquired
defer r.mu.RUnlock() // ← Lock released
names := make([]string, 0, len(r.algorithms))
for name := range r.algorithms {
names = append(names, name)
}
sort.Strings(names)
return names // ← flagged as race
}3. Warning count scales with codebase size
| CI Run | Test Suites | Warnings |
|---|---|---|
| PR #14 (deps update) | 24 | 4,294 |
| PR #15 (TextField) | 24 | 4,567 |
| PR #16 (Overlay/Dropdown) | 24 | 4,616 |
Warnings grow with every new test, uniformly across all 3 platforms:
- Ubuntu: ~1,570
- macOS: ~1,551
- Windows: ~1,495
Reproduction
# Clone the project
git clone https://github.com/gogpu/ui
cd ui
# Run with racedetector — thousands of warnings
racedetector test -v ./...
# Run with standard race detector — zero warnings
go test -race ./...Expected behavior
Sequential tests accessing independent local variables through properly synchronized code should not be flagged as data races.
Environment
- racedetector v0.8.4 (installed via
go install github.com/kolkov/racedetector/cmd/racedetector@latest) - Go 1.25.6 on Ubuntu/macOS/Windows (CI)
- Project: github.com/gogpu/ui (24 packages, 1,500+ tests, 55K LOC)
Suggested root causes
- Missing happens-before for
testing.tRunner: ThetRunnergoroutine join (test N done) → fork (test N+1 start) is not modeled as synchronization - Missing shadow memory reset on allocation: When Go's allocator reuses a heap address, the access history for that address should be cleared
- Missing happens-before for
sync.Mutex:Lock()/Unlock()pairs don't establish happens-before edges in the FastTrack epoch tracking