Main integration project for the spsc buffer library and paranoid API tests.
Quick links:
- Changelog
- Reproducible benchmark and validity protocol
- Quick Start
- Documentation Hub
- Common Concepts
- Concurrency and FreeRTOS
- Method Recipes
- Guard and Bulk Helpers
This is a practical feature matrix against popular and representative SPSC and SPSC-adjacent queues or FIFO helpers from the wider C/C++ ecosystem.
It is intentionally not a throughput ranking. The goal is to show what this project covers as first-class API surface, and what other libraries do or do not cover.
Important comparison rule:
- A checked cell means the capability exists as a documented, built-in part of that library's primary API.
- An empty cell does not mean the behavior is impossible to build on top, only that it is not a first-class built-in feature of that library.
Official links:
spsc- this projectBoost lockfree::spsc_queueETL queue_spsc_atomicETL queue_spsc_lockedrigtorp/SPSCQueuemoodycamel::ReaderWriterQueuefolly::ProducerConsumerQueuePortAudio PaUtilRingBufferJUCE AbstractFifoatomic_queue
Legend:
[x]built in as a first-class API[ ]not part of the library's primary API surface
The ETL column below refers to the SPSC family formed by queue_spsc_atomic and queue_spsc_locked.
| API Model | spsc | Boost | ETL | Rigtorp | Moodycamel | Folly | PortAudio | JUCE | atomic_queue |
|---|---|---|---|---|---|---|---|---|---|
| Simple value enqueue/dequeue API | [x] | [x] | [x] | [x] | [x] | [x] | [ ] | [ ] | [x] |
In-place producer construction API (emplace-style) |
[x] | [ ] | [x] | [x] | [x] | [x] | [ ] | [ ] | [ ] |
True claim() / publish() producer model |
[x] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
Staged producer API (prepare/finished, regions/advance) |
[x] | [ ] | [ ] | [ ] | [ ] | [ ] | [x] | [x] | [ ] |
| Consumer peek/front without immediate consume | [x] | [x] | [ ] | [x] | [x] | [x] | [ ] | [ ] | [ ] |
| Bulk or region-based read/write helpers | [x] | [x] | [ ] | [ ] | [ ] | [ ] | [x] | [x] | [ ] |
| Snapshot or captured-range consumer API | [x] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
| Dedicated blocking queue variant / wait methods | [ ] | [ ] | [ ] | [ ] | [x] | [ ] | [ ] | [ ] | [ ] |
| Capability | spsc | Boost | ETL | Rigtorp | Moodycamel | Folly | PortAudio | JUCE | atomic_queue |
|---|---|---|---|---|---|---|---|---|---|
| Compile-time fixed-capacity form | [x] | [x] | [x] | [ ] | [ ] | [ ] | [ ] | [ ] | [x] |
| Runtime-sized queue form | [x] | [x] | [ ] | [x] | [x] | [x] | [x] | [x] | [x] |
| External caller-owned storage | [x] | [ ] | [ ] | [ ] | [ ] | [ ] | [x] | [x] | [ ] |
| Typed non-owning view container | [x] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
| Raw byte / DMA-slot abstraction | [x] | [ ] | [ ] | [ ] | [ ] | [ ] | [x] | [ ] | [ ] |
Newest-only buffer (latest) |
[x] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
Fixed-array / block wrappers (array_fifo, chunk_fifo) |
[x] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
Separate lifetime-managed queue type (queue) |
[x] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
| Explicit ISR / callback handoff story in official docs | [x] | [ ] | [x] | [ ] | [ ] | [ ] | [x] | [ ] | [ ] |
| Multiple distinct SPSC container models in one library | [x] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
| Library | Power-of-two behavior | Real implication |
|---|---|---|
spsc |
Static capacities are required to be power-of-two; dynamic capacities are normalized to an effective power-of-two geometry | The ring is explicitly designed around mask-based wrap and unambiguous index arithmetic |
Boost spsc_queue |
No documented power-of-two requirement | Treat it as an arbitrary-capacity typed queue unless you choose a power-of-two size for your own reasons |
| ETL SPSC family | No documented power-of-two requirement | Fixed-capacity embedded queues, but not presented as power-of-two-only queues |
rigtorp/SPSCQueue |
Explicitly supports arbitrary non-power-of-two capacities | It intentionally spends one extra slot instead of forcing a power-of-two restriction |
moodycamel::ReaderWriterQueue |
No documented power-of-two requirement | Runtime-sized queue/growing queue model, not documented as power-of-two-constrained |
folly::ProducerConsumerQueue |
No documented power-of-two requirement | Runtime-sized fixed queue, but not documented as a power-of-two-only design |
PortAudio PaUtilRingBuffer |
Element count must be a power-of-two | This is the strongest explicit power-of-two requirement in the compared set |
JUCE AbstractFifo |
No documented power-of-two requirement | It is an index manager over your buffer size, not a power-of-two-specialized ring API |
atomic_queue |
Any fixed size is supported, but power-of-two size enables extra optimizations | It benefits from power-of-two geometry but does not strictly require it |
- Boost, Rigtorp, Moodycamel, Folly, ETL, and
atomic_queueare primarily "one queue API" libraries. They differ in performance goals and ergonomics, but they are still fundamentally queue-centric. - This project is materially different because it is not only a queue. It gives you multiple SPSC transport models under one API family: assignment queue, lifetime-managed queue, typed pool, raw pool, external-storage views, newest-only state handoff, fixed-array wrappers, and chunk/block wrappers.
- If power-of-two geometry is part of your design philosophy, this project and PortAudio are the clearest explicit matches in the compared set.
atomic_queuealso leans into power-of-two optimizations, but does not make them mandatory. - If you need caller-owned memory, only
spsc, PortAudio, and JUCE cover that directly in the compared set. But PortAudio is a C raw ring buffer and JUCE is an index manager over your buffer; neither gives you typed non-owning view containers comparable tofifo_view,pool_view, or the view wrappers here. - If you need a real
claim()/publish()workflow, this project is the only one in this comparison that exposes it as a first-class API model. PortAudio and JUCE provide staged write/read mechanics, but not the same queue abstraction shape. - If you need newest-state semantics, the compared alternatives do not really match
latest. With the others you would emulate that policy above a normal queue; here it is a dedicated container model. - If you need raw DMA-style byte slots with policy-aware alignment and external-storage variants, the closest conceptual neighbor is PortAudio's ring buffer, but PortAudio stays much lower-level and C-oriented.
- ETL is the strongest direct alternative when the target is embedded/ISR handoff and you want a simpler fixed-capacity queue with explicit interrupt-oriented documentation.
- Boost is the strongest direct alternative when you mainly want a mature general-purpose typed SPSC queue with wait-free push/pop and some bulk helpers, but you do not need views, raw pools, or newest-only semantics.
- Rigtorp and Moodycamel are strongest when you mainly want a narrow high-performance typed queue API. They are excellent queue implementations, but they do not try to become a full SPSC container toolkit.
Notes:
- This is a representative set, not a claim that these are literally all SPSC queues on the Internet.
- Some entries are pure SPSC queues, while others are lower-level FIFO primitives that solve adjacent SPSC problems.
- Matrix rows mean "the capability exists somewhere in the library's primary API surface", not necessarily that every type in that library exposes it.
JUCE AbstractFifois fundamentally an index-management helper over user storage, not a typed queue container.PortAudio PaUtilRingBufferis a C single-reader / single-writer raw ring buffer over caller-owned memory, not a family of typed C++ containers.rigtorp/SPSCQueuedoes provide blockingpush/emplacewhen full, but not a separate blocking queue type with explicit wait-style dequeue methods like Moodycamel's blocking variant.- If you only need one typed queue, Boost, ETL, Rigtorp, Moodycamel, Folly, PortAudio, JUCE, or
atomic_queuemay be a better fit simply because they are narrower. - If you need one library to cover typed values, explicit lifetime control, raw DMA-style buffers, external storage views, newest-value handoff, and block wrappers, this project is deliberately broader.
SPSC_HARDENING_ROADMAP.md: the completed 2.0 contract, correctness, test, CI, and performance hardening record.benchmarks/: reproducible, validity-gated measurement harness with a pinned Rigtorp comparator and explicitinconclusiveoutput.src/spsc/: core SPSC library headers (fifo,queue,typed_pool,fifo_view,pool,pool_view,latest,chunk, etc.).src/tests/*_test.cpp: paranoid test suites for each buffer type.spsc_test.pro: Qt/qmake project file.mainwindow.cpp: Qt dashboard that runs suites, showsPASS/FAIL, logs, and timeout status.
Detailed API documentation is in:
src/spsc/README.mdfor the quick start and top-level overviewsrc/spsc/docs/README.mdfor the new split-by-container documentation hubsrc/spsc/docs/concurrency-and-freertos.mdfor task/ISR/FreeRTOS guidance
Prerequisites:
- Qt 6.x with
QtTest - MinGW toolchain (or compatible C++ toolchain configured in Qt)
Typical qmake build flow (Windows/MinGW):
mkdir build
cd build
qmake ..\spsc_test.pro
mingw32-make -j8If you use Qt Creator, opening spsc_test.pro is enough.
Launch the Qt dashboard:
.\bin\debug\spsc_launcher.exe
# or
.\bin\release\spsc_launcher.exeThe dashboard and runner executables are built per configuration:
.\bin\debug\spsc_test_shadow_on.exe --run-suite fifo
.\bin\release\spsc_test_shadow_on.exe --run-suite fifoThe C++20 runner enables SPSC_HAS_SPAN=1; it is separate from the C++17
dashboard matrix so a missing std::span library cannot silently disable its
contracts:
.\bin\debug\spsc_test_cxx20_span.exe --run-suite fifo span_contract
.\bin\debug\spsc_test_cxx20_span.exe --run-suite pool span_contract
.\bin\debug\spsc_test_cxx20_span.exe --run-suite queue raw_bytes_contract
.\bin\debug\spsc_test_cxx20_span.exe --run-suite chunk span_contractH6 also has two standalone policy targets. The first succeeds only when the compiler rejects relaxed atomic publication. Run the second from an x86 Visual Studio Developer PowerShell (or another genuine 32-bit compiler environment):
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\test_relaxed_publication_compile_fail.ps1 -Compiler C:\msys64\ucrt64\bin\g++.exe
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run_h6_32bit_shadow_matrix.ps1 -Compiler clH7 runs each qmake target from a fresh, uniquely named temporary build
directory. It never reads or removes the repository's build/, bin/, MOC,
or object artifacts; use -KeepBuild only when a failed generated tree needs
to be inspected.
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run_h7_matrix.ps1 `
-Configuration Both -Variant all -BuildLauncherOn Linux, the same functional matrix and the standalone sanitizer target are:
bash scripts/run_h7_matrix.sh --qmake qmake6 --configuration both --variant all
bash scripts/run_h7_sanitizers.sh --sanitizer address,undefined
bash scripts/run_h7_sanitizers.sh --sanitizer thread
bash scripts/run_h6_32bit_shadow_matrix.sh --compiler g++run_h7_matrix.ps1 accepts -Qmake, -Make, and -Compiler paths when the
Qt kit is not already configured on PATH. The GitHub Actions workflow runs
the explicit C++17 shadow Debug/Release matrix, C++20 span Debug/Release
targets, Linux sanitizers, genuine 32-bit execution, AArch64 header smoke, and
Windows MinGW/MSVC header smoke.
The dashboard:
- runs each suite in a separate child process
- shows
PASS,FAIL,TIMEOUT, orCRASH - captures the full QtTest log in a text pane
- supports a per-suite timeout from the UI
Main suites include:
fifofifo_viewpoolpool_viewlatestchunkqueuetyped_poolbuffer_pool
Run sources:
build/Desktop_Qt_6_10_1_MinGW_64_bit-Debug/bin/debug/spsc_test_shadow_*.exebuild/Desktop_Qt_6_10_1_MinGW_64_bit-Debug/bin/release/spsc_test_shadow_*.exe
Environment:
- QtTest 6.10.1 / Qt 6.10.1
- GCC 13.1.0
- Windows 11
Matrix:
- Debug and Release
shadow_off,shadow_on,shadow_heurfifo,fifo_view,pool,pool_view,latest,chunk,queue,typed_pool,buffer_pool
Conclusion:
- 54/54 suite runs passed with 30s per-suite timeout.
- The qmake matrix is a C++17 matrix and forces
SPSC_HAS_SPAN=0; C++20std::spanhelpers are intentionally outside this matrix.
Debug death tests use child process spawning (QProcess).
In restricted environments they can fail with:
QProcess: CreateFile failed. (Access is denied.)
In a normal local dev environment these tests should pass.
This project is licensed under the Apache License 2.0.
For the core library sources in src/spsc, the license is also marked directly in file headers using SPDX-License-Identifier: Apache-2.0.
