`cnd_timedwait` waits on a `cnd_t` condition variable with a mutex and an absolute time limit. The calling thread holds the mutex when it enters the wait; the call releases it while waiting and reacquires it before returning. A signal or the time limit can end the wait, but neither proves that the condition the thread needs is true. section 20.5 section 21.2
A one-second limit
`life_wait` obtains the current `TIME_UTC` time with `timespec_get`, adds one to `tv_sec`, and passes the resulting `struct timespec` to `cnd_timedwait`. The argument is an absolute limit, not a duration passed directly to the wait function. section 20.5
int life_wait(cnd_t* cnd, mtx_t* mtx) {
struct timespec now;
timespec_get(&now, TIME_UTC);
now.tv_sec += 1;
return cnd_timedwait(cnd, mtx, &now);
}
The wrapper shows all three wait arguments: the condition variable, the mutex, and the address of the absolute time limit. section 20.5
Keep the wait inside a loop that tests the actual condition. A condition variable identifies what a thread is waiting for; it is not the condition expression itself. In the accounting thread, the loop waits while the game is unfinished and the accounted iteration has caught up with the current iteration. On every return—including a timeout—the thread checks again before proceeding. An untimed wait also requires this check because it may return without a change to the expression. section 20.5
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 the mutex, tests its predicate in a loop, then performs its work and signals the update thread while still in the protected region. section 20.5
TAKEAWAY: Prefer `cnd_timedwait` over `cnd_wait` where an unlimited wait could threaten liveness. A deadline bounds an individual wait; progress still needs an argument about the surrounding predicates and threads. In the accounting and update threads, the relation between their iteration counts supplies that argument: when both repeatedly wait, a return eventually lets one of them advance its count. section 20.7
Entering `cnd_timedwait` is a mutex release operation, and returning from it is a mutex acquire operation. Critical sections protected by that same mutex therefore occur sequentially, with effects from earlier protected sections visible to the returning thread. section 21.2

