πŸ’₯ Asymmetric. Realistic. Glitch-prone. Educational. This is exactly the kind of multithreaded artifact that deserves vault-tier documentation β€” especially under [[asymmetric_pthreads/09_producer_consumer_condition_var]].


πŸ” 09_producer_consumer_condition_var

🧡 Classic 1-buffer producer/consumer using pthread_cond_t and mutex
🧠 One of the only β€œsafe” real-world uses of while in multithreading.
πŸ’€ Mess it up, and you get deadlocks, missed signals, or race conditions you can’t debug.


🧠 Problem: Shared Finite Buffer

  • πŸ‘¨β€πŸ³ Producer: creates ITEM_COUNT items (100)

  • πŸ‘¨β€πŸ‘©β€πŸ‘§ Consumer: consumes each item with delay

  • πŸ“¦ Shared buffer of size 10

  • ⚠️ Critical section = insertion/removal of item

  • πŸ”’ Protected by:

    • pthread_mutex_t mutex

    • pthread_cond_t cond_full

    • pthread_cond_t cond_empty


πŸ“¦ Buffer Logic

int	buffer[BUFFER_SIZE];
int	count = 0;

πŸ’‘ Invariants

RuleMeaning
count == 0Buffer is empty
count == BUFFER_SIZEBuffer is full
0 < count < BUFFER_SIZESafe to read/write

πŸ”§ put_item / get_item

void put_item(int item)
{
	buffer[count] = item;
	count++;
}
int get_item(void)
{
	int item = buffer[count - 1];
	count--;
	return item;
}

⚠️ LIFO behavior.
You’re technically consuming from the top of a stack, not a FIFO queue.
This is not a circular queue. (But perfect for demoing condition variables.)


πŸ§ͺ How It Works

πŸ‘¨β€πŸ³ Producer

while (count == BUFFER_SIZE)
	pthread_cond_wait(&cond_empty, &mutex);
put_item(i);
pthread_cond_signal(&cond_full);
  • πŸš₯ Waits if buffer full

  • 🧠 while is essential β€” thread might get woken up by spurious signal

  • 🧠 Signals consumer when item available


πŸ‘¨β€πŸ‘©β€πŸ‘§ Consumer

while (count == 0)
	pthread_cond_wait(&cond_full, &mutex);
item = get_item();
pthread_cond_signal(&cond_empty);
  • πŸš₯ Waits if buffer empty

  • 🧠 Wakes up only when something to consume

  • 🧠 Signals producer once space is freed


βŒ› Delay Design

#define PRODUCE_DELAY_US 1000
#define CONSUMER_DELAY_US 5000
  • ⏳ Producer is faster than consumer (1ms vs 5ms)

  • πŸ“¦ Buffer fills up fast, forcing pthread_cond_wait β†’ great for demonstrating real blocking


πŸ”¬ Terminal Output (Sample)

Produced 0 (count=1)
Produced 1 (count=2)
...
Produced 9 (count=10)
Consumed 9 (count=9)
Produced 10 (count=10)
...

🧠 You’ll notice:

  • Buffer hits max (count=10)

  • Producer blocks

  • Consumer unblocks it with signal

  • Then it continues

πŸ“Š Visually traceable buffer occupancy


☠️ Failure Modes (If You Do It Wrong)

πŸ”₯ ErrorπŸ’€ Consequence
Use if instead of whileSpurious wake = buffer overrun or underrun
No pthread_mutex_lockRace condition = buffer corruption
Forget to signalDeadlock forever
Stack instead of queueNot FIFO β†’ Logic flaw
Call put_item outside lockUndefined behavior, maybe SIGSEGV

🧠 Why This Is Asymmetric

title: Asymmetry Factors
 
- ⏱️ Designed delays reveal real-time sync issues
- 🧠 Showcases *waiting*, not just mutual exclusion
- πŸ› οΈ Threads are reactive β€” not just aggressive
- πŸ’€ If you remove one `signal()` or change `while` to `if`, deadlock creeps in silently
- πŸ€– Feels stable but is one mistake away from chaos

πŸ”— Suggested Upgrades

πŸ”§ [[asymmetric_pthreads/09b_fifo_ringbuffer_condvar.c]]
   ➜ Convert to true circular queue
 
πŸ”§ [[asymmetric_pthreads/09c_multi_producer_consumer_pool.c]]
   ➜ Expand to thread pool (n producer, m consumer)
 
πŸ’₯ [[asymmetric_pthreads/09d_condvar_lost_signal_demo.c]]
   ➜ Show condition variable signal *missed* β†’ thread starves

🧠 TL;DR Summary

title: Key Concepts
 
- 🧡 Real-world use of `pthread_cond_t`
- 🧠 `while` protects from spurious wakeups
- πŸ”’ Always pair `pthread_cond_wait` with a `pthread_mutex`
- πŸš€ Buffer state transitions: full β†’ wait β†’ empty β†’ wait
- πŸ’£ Critical section correctness matters more than speed

Ready to build [[09b_fifo_ringbuffer_condvar.c]] next?
Or want to inject an artificial deadlock to show what happens with a missing signal?