Lukas' Notes

concurrency

Definition

Data Race

A data race occurs when two or more threads concurrently access the same memory location, at least one access is a write, and no synchronisation mechanism orders the accesses.

Formally, for threads and a shared variable , there is a data race iff

where means the two accesses are separated by a synchronisation edge (happens-before).

Relation to Race Condition

A data race is a condition on access patterns; a race condition is a condition on outcomes.

Data race race condition

A data race implies that the program’s observable behaviour depends on the interleaving of unsynchronised accesses, hence the outcome is nondeterministic — a race condition. The converse does not hold: a race condition can occur even with atomic variables if the logical ordering of updates is uncontrolled.

Race condition without data race

Two threads atomically increment a shared counter. No data race (the increment is atomic), but the final value depends on which thread executes first: the outcome is a race condition. The access pattern is safe; the program logic is not.

Hardware-Level Data Race

Instruction-level interleaving

Even a single high-level statement like *lockvar = 1 decomposes into multiple instructions (load, compare, store). If two threads execute these instruction sequences concurrently without atomicity, their individual instructions can interleave at the microarchitectural level, producing a data race on the lock variable itself.

This is why a lock variable implemented with ordinary loads and stores fails under preemptive multithreading: the read of *lockvar and the subsequent write are not indivisible.

Non-Atomic Access

Non-atomic word write

Assume a 16-bit word is written by copying two 8-bit halves separately. Let and thread write (two’s complement: 1111 1111 1111 1111).

reads between the two half-writes:

should only ever observe or . The intermediate value is a data race artefact: the write was not atomic, so a concurrent read saw a torn state.