Make seeking inside sub circular buffer lock free in Producer Consumer

2026-07-17

Stack Overflow: View Question

Tags: c++, multithreading, qt, operating-system, producer-consumer

Score: 0 | Views: 155

The asker has built a QIODevice wrapper around a background producer thread. The producer pulls data from an upstream source into a circular buffer; the consumer (the GUI thread) reads from it via the standard Qt API. To make seek() cheap without re-reading, they maintain a secondary circular index of buffer offsets, and they want the whole seek path to be lock-free.

Why it's harder than a textbook SPSC queue. A classic single-producer/single-consumer ring buffer is lock-free because there is exactly one writer of head and one writer of tail. Seeking breaks that clean split: the consumer wants to reposition its read cursor to an offset that may be anywhere inside the buffer, and it must simultaneously validate that (a) the offset is still resident (the producer hasn't overwritten it) and (b) the sub-index it consulted describes the buffer as it stands right now. Between reading the index and moving the cursor, the producer can advance head and invalidate everything.

A workable direction. Treat the problem as a versioned snapshot rather than a lock. Roughly:

Gotchas. ABA on the sub-index itself is real — reusing slots without a generation counter will let a stale entry look valid. QIODevice is fundamentally synchronous, so a seek that races with overwrite has to have a defined failure mode; silently returning stale bytes is worse than returning an error. Also watch for torn reads on 64-bit counters on 32-bit platforms — use std::atomic<uint64_t>, not a plain member. Finally, Qt signals across threads are queued, so any "buffer advanced" notification the consumer relies on is inherently stale by the time it's processed — the atomic counter must be the source of truth, not the signal.

The challenge: Lock-free seeking requires reasoning about a moving overwrite frontier, not just producer/consumer indices — the seek target itself can vanish mid-operation.

All newsletters