Data race

title
Data race
type
concept
summary
Unordered concurrent access to one memory location with at least one write; distinct from a general race, and what lockset and vector-clock detectors hunt
tags
concurrency, debugging, memory-models
created
2026-09-13
updated
2026-09-13

A data race is two accesses to the same memory location from different threads, at least one of them a write, with nothing ordering one before the other. C11 states it as two conflicting actions in different threads, at least one not atomic, where neither happens before the other, and makes the result undefined behaviour. Golang's memory model says a write concurrent with another read or write of the same location, unless all the accesses go through sync/atomic.

The definition is about ordering, not about bugs. A program with a data race may pass every test, as the counter example in threadsanitizer-limits does until N=10000, but it contains a latent bug whether or not a test notices.

Data race versus general race

A general race (race condition) is a correctness bug that depends on scheduling. The two overlap without being the same thing. The standard illustration is a read-modify-write on a counter, v = counter; counter = v + 1. Without a lock it is a data race and a general race. Put the read and the write in two separate critical sections and the data race is gone, because every access is now ordered by the mutex, but increments are still lost. No data-race detector reports that version, because there is nothing for it to report.

This distinction is why tools built for one leave a large share of concurrency bugs untouched. In Tu et al's study of real Golang bugs (see message-passing-shared-mutable-state) the race detector caught roughly half of the non-blocking bugs. Channels remove data races without removing coordination bugs, which is the argument in message-passing-vs-shared-memory and the taxonomy in go-channel-bug-patterns.

How detectors find them

Data races can in principle be detected automatically, with no annotations from the programmer. Dynamic detectors instrument every memory access and the synchronization primitives, then check each observed execution.

Lockset detectors, starting with Eraser (1997), record which locks are held on each access to a shared location and report when the intersection of those sets becomes empty. They catch races that the observed interleaving happened not to expose, but they don't understand ordering idioms like thread join, which produces false positives, and they cannot see a race in code that takes no locks at all.

Happens-before detectors compute the ordering relation directly. FastTrack gives each thread a logical clock and a vector clock of what it knows about the others, merges vectors on acquire and publishes them on release, and flags an access whose last conflicting access isn't covered by the current thread's vector. ThreadSanitizer v2 and v3 work along these lines and are what Clang, GCC, Golang, Swift and OCaml use.

A happens-before detector only knows the orderings the run actually created, and that is its main weakness. An unrelated mutex taken in a lucky order creates an edge between two unsynchronized writes. TSan also bounds its own memory with fixed budgets (255 thread slots, 14-bit clocks, four access records per 8-byte granule), and Golang's sync.Pool deliberately feeds the detector synthetic orderings. threadsanitizer-limits shows each one hiding an obvious race in C and Golang.

Why language models forbid them

In shared-memory-consistency-causality the absence of data races is the condition that makes a simple memory model possible: sequential consistency for atomic operations holds provided non-atomic accesses are data-race-free (SC+DRF), which Java and Golang rely on. An unsynchronized access is a side channel through which the hardware's real, weaker ordering becomes observable. That is also why the C and C++ standards classify data races as undefined behaviour instead of assigning them a result: a defined result would require every architecture to hide a lack of write-atomicity.