mtx_lock is a C threads-library function that blocks the calling thread until it acquires the referenced mutex. Acquisition excludes other threads from critical sections protected by that mutex; mtx_unlock releases it. section 20.4
Protecting shared game data
An input thread locks L->mtx before calling life_birth9(L), which modifies the shared game data, and unlocks the mutex afterward. Other input cases access only atomic objects and do not use this mutex. section 20.4
Listing 20.1 The input thread function of B9
int input_thread(void* Lv) {
termin_unbuffered();
life*restrict L = Lv;
constexpr size_t len = 32;
char command[len];
do {
auto c = getchar();
command[0] = c;
switch(c) {
case GO_LEFT : life_advance(L, 0, -1); break;
case GO_RIGHT: life_advance(L, 0, +1); break;
case GO_UP : life_advance(L, -1, 0); break;
case GO_DOWN : life_advance(L, +1, 0); break;
case GO_HOME : L->x0 = 1; L->x1 = 1; break;
case ESCAPE :
ungetc(termin_translate(termin_read_esc(len, command)), stdin);
continue;
case '+': if (L->frames < 128) L->frames++; continue;
case '-': if (L->frames > 1) L->frames--; continue;
case ' ':
case 'b':
case 'B':
mtx_lock(&L->mtx);
// VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV 1
life_birth9(L);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cnd_signal(&L->draw);
mtx_unlock(&L->mtx);
continue;
case 'q':
case 'Q':
case EOF: goto FINISH;
} 2
cnd_signal(&L->draw);
} while (!(L->finished || feof(stdin)));
FINISH:
L->finished = true;
return 0;
}
Inspect the input-thread listing to see mtx_lock placed immediately around the shared-data operation. section 20.4
Key synchronization properties:
- Successful acquisition follows a mutex release and makes effects from earlier critical sections protected by that mutex visible. section 21.2
- Use mtx_trylock to avoid waiting, or mtx_timedlock to wait for a bounded period when the mutex supports timed locking. section 20.4
