liveBook · Manning

Critical region

As explained in Modern C

A critical section is a region of execution that accesses shared state under synchronization so that conflicting concurrent accesses are excluded.

Accounting for a changing board

An accounting thread must scan a shared position matrix while other threads may update it. It locks L->mtx, waits until an iteration needs accounting, calls life_account(L), signals the update thread, and unlocks the mutex. The scan is part of a critical section: it cannot safely overlap a conflicting access to the same protected data. section 18.5

A critical section is a code path that operates on shared data not protected by atomic operations. A mutex of type mtx_t protects it. mtx_lock waits until no other thread is inside a section protected by that same mutex; mtx_unlock releases the lock. TAKEAWAY: Threads that access the protected data must coordinate through the same mutex. section 18.4 section 18.7

The boundary depends on the work. The input thread needs a lock when a command changes a cluster of board cells, but its other cases interact only with atomic objects. Accounting, updating, and drawing each place most of a loop body inside a critical section. Arrays cannot be declared _Atomic here, making a mutex the practical protection for the larger board data. section 18.4 section 18.5

Listing 18.2. The account thread function of B9
 99   int account_thread(void* Lv) {
100     life*restrict L = Lv;
101     while (!L->finished) {
102       // Blocks until there is work
103       mtx_lock(&L->mtx);
104       while (!L->finished && (L->accounted == L->iteration))
105         life_wait(&L->acco, &L->mtx);
106
107       // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
108       life_account(L);
109       if ((L->last + repetition) < L->accounted) {
110         L->finished = true;
111       }
112       // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
113
114       cnd_signal(&L->upda);
115       mtx_unlock(&L->mtx);
116     }
117     return 0;
118   }


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":1},{\"line\":0,\"ch\":3}],[{\"line\":0,\"ch\":6},{\"line\":0,\"ch\":9}],[{\"line\":0,\"ch\":25},{\"line\":0,\"ch\":29}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":3}],[{\"line\":1,\"ch\":13},{\"line\":1,\"ch\":21}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":3}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":13}],[{\"line\":5,\"ch\":10},{\"line\":5,\"ch\":15}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":3}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":3}],[{\"line\":4,\"ch\":10},{\"line\":4,\"ch\":18}],[{\"line\":5,\"ch\":0},{\"line\":5,\"ch\":3}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":13}],[{\"line\":5,\"ch\":10},{\"line\":5,\"ch\":15}],[{\"line\":6,\"ch\":0},{\"line\":6,\"ch\":3}],[{\"line\":7,\"ch\":0},{\"line\":7,\"ch\":3}],[{\"line\":8,\"ch\":0},{\"line\":8,\"ch\":3}],[{\"line\":9,\"ch\":0},{\"line\":9,\"ch\":3}],[{\"line\":10,\"ch\":0},{\"line\":10,\"ch\":3}],[{\"line\":1,\"ch\":9},{\"line\":1,\"ch\":11}],[{\"line\":6,\"ch\":13},{\"line\":6,\"ch\":15}],[{\"line\":9,\"ch\":11},{\"line\":9,\"ch\":13}],[{\"line\":10,\"ch\":10},{\"line\":10,\"ch\":12}],[{\"line\":11,\"ch\":0},{\"line\":11,\"ch\":3}],[{\"line\":11,\"ch\":26},{\"line\":11,\"ch\":30}],[{\"line\":12,\"ch\":0},{\"line\":12,\"ch\":3}],[{\"line\":13,\"ch\":0},{\"line\":13,\"ch\":3}],[{\"line\":14,\"ch\":0},{\"line\":14,\"ch\":3}],[{\"line\":15,\"ch\":0},{\"line\":15,\"ch\":3}],[{\"line\":15,\"ch\":10},{\"line\":15,\"ch\":20}],[{\"line\":16,\"ch\":0},{\"line\":16,\"ch\":3}],[{\"line\":16,\"ch\":10},{\"line\":16,\"ch\":20}],[{\"line\":17,\"ch\":0},{\"line\":17,\"ch\":3}],[{\"line\":18,\"ch\":0},{\"line\":18,\"ch\":3}],[{\"line\":18,\"ch\":8},{\"line\":18,\"ch\":14}],[{\"line\":19,\"ch\":0},{\"line\":19,\"ch\":3}]]"}
!@%STYLE%@!

Follow the accounting thread from mtx_lock through its wait loop and matrix scan to cnd_signal and mtx_unlock; the marked computation lies within the protected region. section 18.5

Waiting for work is different from protecting the work. A condition variable identifies what a thread waits for; a condition expression determines whether it can proceed. The accounting thread tests that expression in a while loop because a wait may return without making the condition true. Its timed wait uses both a condition variable and the mutex; a waiting thread reacquires the mutex before continuing. section 18.5

Mutex operations also provide linearizability: after acquiring a mutex, a thread can rely on operations completed before other threads released that same mutex having taken effect. mtx_trylock offers a test that can avoid waiting; mtx_timedlock limits how long the caller waits. section 18.4

How Modern C covers it

How deep it goes
Explains lock boundaries, exclusion, linearizability, and waiting through thread routines, without developing mutex implementation internals.
Examples
Traces a board-edit command and the accounting, update, and draw loops around a shared position matrix.
What it assumes
Assumes familiarity with C threads and shared objects while defining mutex protection and condition-variable roles in context.
What you can do after
Apply it via C mutexes to protect shared board operations and identify where waiting for work fits within the protected flow.
How it connects
Connects mutexes and atomic objects to shared data, and condition variables to coordinated processing.

Also explained in

Compare how 9 books explain critical region →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C. The section links point to the parts of the book it draws on.