Race Conditions
When the result depends on an unlucky interleaving of concurrent accesses.
Level: intermediate
What is it?
A race condition occurs when the correctness of a program depends on the timing or ordering of concurrent events. A data race is a narrower case involving unsynchronized conflicting memory accesses, at least one of which writes, under a language's memory model.
Why does it matter?
Race conditions create intermittent corruption, lost updates, stale reads, and security bugs. Because the failure depends on scheduling and load, a bug may disappear under a debugger and return in production.
Mental model
Imagine two workers editing the same spreadsheet cell after reading the same old value. Each calculates a new value correctly in isolation, but the last write erases the other's update.
How it works
- A read-modify-write sequence is often multiple operations rather than one atomic operation.
- Mutual exclusion can protect a compound invariant; atomics can protect simple state transitions when their semantics suffice.
- Race freedom requires a defined synchronization relationship and correct protection for all accesses to the shared state.
- Thread sanitizers and stress tests help find races but cannot prove the absence of all concurrency errors.
- A race can also involve non-memory events, such as checking a file and then opening it after the file changes.
Steps
- A shared counter starts at 0.
- Thread A reads 0; thread B also reads 0 before either writes.
- Both compute 1 and write 1, so two increments produce a final value of 1 instead of 2.
- Use an atomic increment or protect the read-modify-write sequence with a mutex.
Real-world usage
- Debugging flaky multithreaded tests.
- Reviewing check-then-act security patterns and file access.
- Designing concurrent caches, counters, queues, and registries.
Common mistakes
- Assuming an operation is atomic because it is one source-code line.
- Testing only under light load or one CPU core.
- Locking the write but not the preceding read that participates in the invariant.
Interview questions
Why can two increments result in only one increment?
Both threads may read the same old value and then overwrite each other's computed result; the compound read-modify-write was not atomic.
What is the difference between a race condition and a data race?
A race condition is a broad correctness dependency on event order. A data race is specifically conflicting unsynchronized memory access under a language memory model.