← Concepts

阅读中文版本

Data Race Detection

Data race detection finds two concurrent accesses to the same location where at least one is a write, the bug class that produces nondeterministic corruption that only surfaces under specific.

concurrency

Definition

Data race detection is the analysis — static (type system, ownership), dynamic (ThreadSanitizer, fast-track), or model-based — that identifies two or more concurrent accesses to the same memory location where at least one is a write and no synchronization orders them, the canonical undefined behavior in shared-memory concurrency.

Context and scope

Applied in compiled language runtimes (TSan, Helgrind), Rust borrow checker, and any concurrency-heavy codebase where races are the dominant defect class.

Common misconceptions

  • A data race is the same as a race condition — a data race is unsynchronized access to memory; a race condition is the broader semantic bug where outcome depends on interleaving, which can occur without a data race
  • Testing under load finds races — it samples a vanishing fraction of interleavings; detection tools reason about the happens-before graph, which is exhaustive

Anti-patterns

  • Running the test suite under TSan and treating every warning as a bug, when some are benign races on idempotent flags
  • Disabling race detection in CI because it slows the tests, letting races merge and surface only in production

Related concepts

Authoritative source

https://llvm.org/devmtg/2017-10/slides/Serebryany-ThreadSanitizer.pdf

What it is

Data race detection is the analysis — static (type system, ownership), dynamic (ThreadSanitizer, fast-track), or model-based — that identifies two or more concurrent accesses to the same memory location where at least one is a write and no synchronization orders them, the canonical undefined behavior in shared-memory concurrency. Applied in compiled language runtimes (TSan, Helgrind), Rust borrow checker, and any concurrency-heavy codebase where races are the dominant defect class. The central difficulty is that interleaving is nondeterministic: the set of possible executions grows super-exponentially with the number of operations, and testing samples a vanishing fraction.

How it works

Correctness is established by reasoning about happens-before relationships, not by running more tests. Synchronization primitives are chosen for the specific guarantee needed — mutual exclusion, publication, or ordering — and composed only with a documented proof of absence of deadlock. Bounded resources apply backpressure rather than unbounded queuing that hides overload until memory exhausts.

When to apply and pitfalls

Apply whenever multiple flows share mutable state or resources. The dominant pitfall is reasoning sequentially about concurrent code — “this can’t happen because A finishes before B” — when in fact the scheduler can interleave at any instruction boundary. The second is using a coarse lock for simplicity and turning parallelism back into serialization.