Synchronization PatternsCounting SemaphoresMedium⏱️ ~2 min

Common Patterns: Bounded Buffer and Resource Pool

Core Pattern
Counting semaphores shine in two patterns: bounded buffers (tracking empty/full slots) and resource pools (tracking available resources).

Pattern 1: Bounded Buffer

A queue with fixed capacity N. Use TWO semaphores: empty (initialized to N) tracks empty slots, full (initialized to 0) tracks filled slots. Producer waits on empty, signals full. Consumer waits on full, signals empty.

Bounded Buffer: Two Semaphores
Producerwait(empty)Buffercapacity: NConsumerwait(full)signal(full)signal(empty)

Pattern 2: Resource Pool

A pool of N identical resources (connections, threads, licenses). Single semaphore initialized to N. Thread waits to acquire, signals to release. Simple and elegant.

Why Two Semaphores for Bounded Buffer?

Producers need to wait when buffer is FULL (no empty slots). Consumers need to wait when buffer is EMPTY (no full slots). Two different conditions require two different semaphores. They work together, moving in opposite directions.

Invariant: empty + full = N (always). When producer takes an empty slot, consumer gains a full slot, and vice versa.

Design Rule: One counting semaphore per condition you need to wait on. Bounded buffer has two conditions (buffer not full, buffer not empty), so two semaphores.
💡 Key Takeaways
Bounded buffer uses two semaphores: empty (tracks free slots) and full (tracks filled slots).
Producer: wait(empty), add item, signal(full). Consumer: wait(full), remove item, signal(empty).
Resource pool uses one semaphore: tracks available resources. wait() to acquire, signal() to release.
Invariant in bounded buffer: empty + full = capacity. They move in opposite directions.
One semaphore per distinct blocking condition. Multiple conditions need multiple semaphores.
📌 Examples
1Bounded buffer (N=10): empty starts at 10, full starts at 0. After 4 items added: empty=6, full=4.
2Connection pool (N=5): semaphore starts at 5. After 5 connections acquired: semaphore=0, next request blocks.
3Rate limiter: semaphore initialized to requests-per-second. Each request waits. Periodic timer signals.
← Back to Counting Semaphores Overview