A thread updating a shared array cannot protect the whole array with _Atomic: C has no atomic array types. Instead, put the access inside a critical section protected by a mutex. In C, mtx_t is the mutex type supplied by <threads.h>. section 20.4 section 20.1
Protect a shared update
When an input thread adds a cluster of cells to a shared game board, it calls mtx_lock(&L->mtx), updates the board, then calls mtx_unlock(&L->mtx). Other input commands touch only atomic objects and do not need that critical section. section 20.4
mtx_lock waits until the calling thread can acquire the mutex; mtx_unlock releases it. Critical sections using the same mutex occur sequentially. Unlocking is a release operation, and successful locking is an acquire operation: a thread entering a protected section sees the effects of earlier sections protected by that mutex. TAKEAWAY: Use the same mutex for every critical section that relies on this protection. section 20.4 section 21.2
Choose the operation to match the wait:
- mtx_lock waits to acquire the mutex; mtx_trylock tests whether it can be acquired without waiting; mtx_timedlock limits how long to wait. section 20.4
- For work that depends on another thread, use a condition variable with the mutex. A condition wait releases the mutex and reacquires it before returning. Recheck the condition expression in a loop: returning does not establish that the expression changed. section 20.5 section 21.2
- If a critical section needs multiple mutexes, acquire them in the same order across threads. Opposite acquisition orders can leave two threads each waiting for a mutex held by the other. section 20.7
Listing 20.2 The account thread function of B9
int account_thread(void* Lv) {
life*restrict L = Lv;
while (!L->finished) {
// Blocks until there is work
mtx_lock(&L->mtx);
while (!L->finished && (L->accounted == L->iteration))
life_wait(&L->acco, &L->mtx);
// VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
life_account(L);
if ((L->last + repetition) < L->accounted) {
L->finished = true;
}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cnd_signal(&L->upda);
mtx_unlock(&L->mtx);
}
return 0;
}
The accounting thread locks before checking whether work is ready, waits in a condition-testing loop, updates shared state, signals the update thread, and unlocks. section 20.5
Locking protects shared data, but it does not by itself guarantee that a threaded program will keep making progress. Deadlock and livelock need separate consideration. Timed condition waits are preferred here over untimed waits to avoid a thread remaining blocked indefinitely. section 20.7

















