spsc_queue¶
Overview¶
prototype::spsc_queue<T, N> is a lock-free, wait-free, single-producer/single-consumer bounded queue. Both push and pop are guaranteed to complete in constant time without blocking.
When to Use¶
- Thread-to-thread communication (exactly one producer, one consumer)
- Audio/video processing pipelines
- Logging (application thread → log writer thread)
- Interrupt handler → main loop communication (embedded)
API Reference¶
namespace prototype {
template <typename T, size_t N>
class spsc_queue {
public:
spsc_queue();
// Producer interface (call from producer thread only)
bool push(const T& value);
bool push(T&& value);
template <typename... Args>
bool emplace(Args&&... args);
// Consumer interface (call from consumer thread only)
bool pop(T& value);
// Query (approximate, may be stale)
bool empty() const;
bool full() const;
size_t size() const; // Approximate
static constexpr size_t capacity() { return N; }
};
} // namespace prototype
Example Code¶
#include <prototype/concurrency/spsc_queue.hpp>
// 1024-element queue between producer and consumer threads
prototype::spsc_queue<int, 1024> event_queue;
// Producer thread
void producer() {
for (int i = 0; i < 10000; ++i) {
while (!event_queue.push(i)) {
// Queue full back off or spin
}
}
}
// Consumer thread
void consumer() {
int value;
while (running) {
if (event_queue.pop(value)) {
process(value);
}
}
}
Performance Characteristics¶
| Operation | Complexity | Latency | Notes |
|---|---|---|---|
push |
O(1) | ~10-20ns | Wait-free |
pop |
O(1) | ~10-20ns | Wait-free |
empty/full |
O(1) | ~5ns | Approximate |
Throughput¶
| Scenario | Throughput |
|---|---|
| Uncontended | 60-80M ops/sec |
| Contended (alternating) | 40-50M ops/sec |
Safety Notes¶
Single producer, single consumer
This queue is ONLY safe when exactly one thread calls push and exactly one thread calls pop. Multiple producers or multiple consumers require external synchronization or a different data structure.
Bounded capacity
The queue has fixed capacity N. push returns false when full (never blocks). pop returns false when empty (never blocks).
Design Notes¶
- Implementation: Circular buffer with separate head (consumer) and tail (producer) indices
- Cache-line padding between head and tail to prevent false sharing
- Head and tail are atomic with acquire/release semantics (no seq_cst)
- Capacity is N (not N-1) uses a size counter or wrapping trick
sizeof(spsc_queue<int, 1024>)≈ 4KB + 128 bytes (cache-line padded indices)- Wait-free: both push and pop always complete in bounded steps (no retry loops)