The If That Lies

A one-slot bounded buffer with one producer and two consumers. get() asserts count == 1 before taking the item.

void *producer(void *arg) {
    for (int i = 0; i < loops; i++) {
        Mutex_lock(&mutex);            // p1
        if (count == 1)                // p2
            Cond_wait(&cond, &mutex);  // p3
        put(i);                        // p4
        Cond_signal(&cond);            // p5
        Mutex_unlock(&mutex);          // p6
    }
}

void *consumer(void *arg) {
    for (int i = 0; i < loops; i++) {
        Mutex_lock(&mutex);            // c1
        if (count == 0)                // c2
            Cond_wait(&cond, &mutex);  // c3
        int tmp = get();               // c4
        Cond_signal(&cond);            // c5
        Mutex_unlock(&mutex);          // c6
    }
}
step Tc1 Tc2 Tp count
1 c1, c2, c3: buffer empty, sleeps ready ready 0
2 sleeping ready p1, p2, p4: fills the buffer 1
3 ready, woken by the signal ready p5 signals, p6 unlocks 1
4 ready, not yet scheduled c1, c2, c4: takes the item ready 0
5 returns from wait, runs c4 ready ready 0

What happens at step 5?

A

Tc1 takes the same item Tc2 already took, so the value is delivered twice and count goes to -1.

B

The assertion inside get() fires. Tc1 was woken as a hint that the buffer had filled, but Tc2 emptied it in the gap before Tc1 was scheduled.

C

Tc1 blocks inside get() until the producer refills the buffer.

D

Nothing goes wrong. Cond_signal hands the lock and the filled buffer directly to Tc1, so Tc2 could not have run at step 4.