~/problems / Pools & pipelines / Ring buffers and producer/consumer pipelines

Basics: blocking ring buffer

easy basics ~10 min

A producer thread hands items to a consumer thread through a fixed-size circular buffer. When the buffer is full the producer has to wait (back-pressure); when it's empty the consumer has to wait.

Implement RingBuffer(capacity):

  • Store the items in self.buf, a list of length capacity created once in __init__. Track the read position and the number of items yourself, wrapping around with % capacity. No deque, queue.Queue, pop(0) or growing the list.
  • put(item): block while the buffer is full, then add item at the back.
  • get(): block while the buffer is empty, then remove and return the item at the front.
  • __len__(): the number of items currently stored.

Items come out in the order they went in. Any value can be an item, including 0 and None.

rb = RingBuffer(2)
rb.put("a"); rb.put("b")   # full: a third put() would block
rb.get()                   # "a", frees a slot
rb.put("c")                # goes into slot 0 (wrap-around)
rb.get(), rb.get()         # "b", "c"; now empty, so the next get() blocks

Constraints: capacity >= 1. Several producers and consumers may use it at once. Don't sleep or busy-wait.

Show hint

Write to buf[(head + size) % capacity], read from buf[head] and advance head = (head + 1) % capacity; keep size so full (size == capacity) and empty (size == 0) can't be confused, and guard it all with one Condition using wait_for + notify_all.

Topic: Ring buffers and producer/consumer pipelines. Fixed-size circular buffers, head/tail indexes, back-pressure.

0:00
Ctrl ' run · Ctrl ↵ submit
esc