Race Conditions and Data Races
This chapter distinguishes race conditions (logic bugs from timing) from data races (concurrent access to a non-atomic object, which is undefined behavior).
Why This Matters
"Race" is used loosely, but in C a *data race* has a precise meaning and is UB. A race condition can exist even with no data race. Knowing the difference determines whether your code is merely buggy or fundamentally undefined.
Prerequisites
c.conc.1— processes, threads, shared state.
Core Concept
Data race
Two operations conflict if they access the same memory location and at least one is a write. A data race occurs when two conflicting operations on a non-atomic object are executed by different threads without a happens-before relationship.
Data races are undefined behavior. VERIFIED
Race condition
A race condition is a logic flaw where the program's outcome depends on the timing/order of events. It can occur even when all accesses are properly synchronized (e.g., two threads each check a flag and act — the *interleaving* of check-then-act is the bug).
Examples
Data race (UB)
#include <threads.h>
static int shared = 0;
int writer(void *arg)
{
(void)arg;
shared = 1; /* write */
return 0;
}
int reader(void *arg)
{
(void)arg;
int v = shared; /* read, no synchronization -> data race */
return v;
}
Race condition (check-then-act)
static atomic_int flag = 0;
int init_once(void *arg)
{
(void)arg;
if (atomic_load(&flag) == 0) { /* check */
/* ... initialize ... */
atomic_store(&flag, 1); /* act */
}
return 0;
}
Two threads can both see flag == 0 before either stores, running the initialization twice — a race condition, even though each atomic access is well-defined.
How It Works
The C memory model defines a happens-before relation. Synchronization (mutexes, atomics with proper order) establishes it. Without it, conflicting accesses are a data race and UB. Race conditions are higher-level timing bugs, independent of the memory model.
Variations
Synchronized race condition
A race condition can persist even with correct locking if the *algorithm* is not atomic across multiple operations (check-then-act, read-modify-write across separate statements).
Benign races
Some code uses volatile or relaxed atomics for flags where the logic is eventually-consistent; this is still a data race if the object is non-atomic.
Common Mistakes
- Using
volatileto "fix" a data race (it does not). - Thinking a mutex around each individual access prevents a check-then-act
race.
- Using
atomicloads/stores but with the wrong memory order and then
reasoning about surrounding non-atomic data.
Undefined Behavior
- A data race on a non-atomic object is UB.
VERIFIED
Portability
- The data-race definition and the memory model are standard (C11+). Race
conditions are language-independent logic bugs.
Under the Hood
ThreadSanitizer (c.debug.tsan) detects data races at run time by tracking accesses and the happens-before graph. Race conditions require reasoning about interleavings.
Practical Usage
- Protect shared non-atomic data with a mutex or use atomics.
- Make multi-step operations atomic (lock around the whole sequence, or use a
single atomic compare-exchange).
- Run TSan to catch data races.
Exercises
1. Write a data race and detect it with TSan. 2. Write a check-then-act race condition (no data race) and explain it. 3. Fix both examples: one with a mutex, one with an atomic compare-exchange.
Deep Challenge
Explain precisely why volatile does not prevent a data race, citing the C memory model. Then show the correct atomic or mutex-based fix.
Related Concepts
c.conc.1— shared state.c.conc.5— atomics and memory model.c.debug.tsan— TSan.
References
- ISO C §5.1.2.4 (data races), §7.17 (atomics).
Verification
- Data race definition and UB status.
VERIFIED - Race condition vs. data race distinction.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Data race definition
- [ ] Race condition definition
- [ ] Happens-before
- [ ] volatile is not a fix
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.conc.race | 0 | 6 |
| c.conc.data-race | 0 | 7 |