# atomic_queue: C++14 Lock-Free Queues for Fixed-Capacity Handoffs

> atomic_queue is a C++14 library of lock-free ring-buffer queues for low-latency producer and consumer handoffs. Its fixed capacity, value semantics, and lack of blocking operations suit bounded in-memory workloads, but not queues that must grow dynamically or wait for consumers.

**max0x7ba/atomic_queue** — C++14 concurrent lock-free low-latency queue.

- Repository: https://github.com/max0x7ba/atomic_queue
- Website: https://max0x7ba.github.io/atomic_queue/html/benchmarks.html
- Stars: 1,903 · Forks: 222
- Language: C++
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/max0x7ba-atomic-queue

## Why atomic_queue Exists for Bounded Thread Handoffs

atomic_queue addresses a narrow but demanding part of concurrent C++ design: passing small values between threads while minimizing the delay between a producer's `push` and a consumer's `pop`. The README describes multiple-producer-multiple-consumer lock-free queues built around ring buffers and `std::atomic`. Its stated emphasis is push-to-pop latency, not general-purpose message brokering or an unbounded work queue.

The intended users are engineers working on trading systems, telemetry paths, event delivery, and other systems where application threads need an in-memory handoff without entering the operating system. The README also describes inter-process communication through shared memory for non-`B` variants. Those variants are described as position-independent, fixed-capacity queues without heap allocation, internal pointers, or internal references, which makes them candidates for storage placed in shared memory.

The design has a clear boundary: in-flight work must have a sensible upper bound. A queue that continuously accumulates items is not made safer by allowing its ring to expand without limit. The README argues that growth can eventually consume memory across the wider system and treats excessive queue depth as a sign that consumers cannot keep pace. Applications must reject, overwrite, spill, or otherwise handle a full queue themselves because the library does not provide an OS-blocking push operation.

## Ring Buffers, Atomic Slots, and Value Semantics

The core architecture is a linear, fixed-capacity ring-buffer array. Queue metadata and elements are positioned to avoid contention and false sharing, while the queue limits atomic instructions around the common push and pop paths. The repository presents atomic_queue as a C++14 library based on `std::atomic`.

Its queue types provide value semantics. According to the README, `push` copies or moves its argument and `pop` copies or moves the stored value. The queue does not retain references or pointers to the arguments after those calls return, and callers cannot obtain a reference or pointer to an element inside the ring buffer. This removes a class of lifetime and aliasing mistakes associated with queues that publish access to an internal slot while another thread still owns it.

The fixed ring also avoids the linked-list memory reclamation problem that the README associates with linked queues. That does not make memory management free in every sense. Capacity is reserved when the queue object is constructed, and there are no heap allocations after construction. The application must still ensure that queued types can be copied or moved safely across its chosen producer and consumer execution model. The README does not document allocator support, element traits, or platform-specific readiness checks.

The repository states that the queue has been developed, tested, and benchmarked on Linux x86_64. It expects other C++14 platforms implementing `std::atomic` to compile and pass its unit tests. GitHub Actions coverage listed by the README includes Linux on x86_64, ARM, RISC-V, PowerPC, IBM System/390, and LoongArch, plus Windows x86_64 and macOS ARM. Some cross-architecture jobs run in QEMU. This is broader than a Linux-only source tree, but it does not amount to confirmation for every compiler and CPU.

## Installing and Checking the Local Build

The README points to Conan Center and vcpkg through package badges, naming `atomic_queue` for Conan and `atomic-queue` for vcpkg. It does not provide installation commands for either package manager, so the exact package-manager tutorial cannot be reproduced without guessing flags. The repository also supplies a `Makefile` whose usage examples assume the source is in `~/src/atomic_queue`.

For a first local check using the documented Makefile workflow, run its unit-test target. This command builds the configured project and executes `run_tests`. It is a repository validation step rather than an installation command.

```bash
/bin/time make -C ~/src/atomic_queue -Rj$(($(nproc)/2)) run_tests
```

The Makefile also shows build selections such as `TOOLSET=clang-20`, `BUILD=debug`, and `BUILD=sanitize`. Those values belong to the project's usage examples and should only be chosen when that compiler is installed. The README does not supply a minimal application that constructs a queue, starts producer and consumer threads, and checks the first pushed and popped value. A first real application therefore requires checking the complete queue headers under `include/` and the programs under `src/` for the exact class and API.

## Nonblocking Capacity as an Application Constraint

atomic_queue does not include OS-blocking push or pop functions. That is not an incidental omission. It follows directly from the project's latency target. The README states that handing a spinning or waiting operation to an operating-system blocking primitive adds wake-up delay, so the queue avoids kernel calls in its handoff path.

This choice works well when producers and consumers are already scheduled and a bounded amount of temporary backlog is acceptable. It does not define a general waiting policy for a producer that finds the ring full or a consumer that finds it empty. The application must inspect the return behavior exposed by the selected API and choose its own retry, failure, drop, overwrite, or alternate routing policy. The README does not document bounded-spin helpers as substitutes for blocking operations.

The queue is therefore wrong for workflows in which work arrives unpredictably and must wait indefinitely until a worker is available. Durable logging, database-style buffering, and job submission often need backpressure or persistence beyond a fixed memory ring. A queue based on `std::mutex` and a condition variable can wait for capacity or data, although it introduces operating-system scheduling and wake-up costs. An intrusive linked queue can allow dynamic capacity, but that design must address pointer lifetime and memory reclamation, which the ring intentionally avoids.

## Choosing SPSC, MPMC, and Shared-Memory Queue Types

The project is aimed at both single-producer/single-consumer and multiple-producer/multiple-consumer patterns. These are different contention and ownership models, so selecting a queue class by its label alone is not enough. The README excerpt does not provide the complete class list or method signatures. The public headers under `include/` are the place to determine which constructor forms, capacity rules, and element restrictions apply.

The fixed-capacity requirement can be met at compile time or construction time. Compile-time selection suits pipelines where the maximum outstanding message count is part of the type configuration. Construction-time capacity is more flexible, but it still requires the caller to decide how many elements may exist at once. Capacity should cover the worst expected producer lead over consumers, not merely the normal average, because the library has no blocking growth mechanism.

For shared-memory use, the README identifies non-`B` variants as position-independent and free of internal pointers and references. It also says they perform no heap allocation after queue construction. Those properties matter when separate processes access the same ring. The README does not document process initialization, memory ordering at the process boundary, resource ownership, crash recovery, or whether arbitrary C++ objects can be shared between processes. Those points must be checked in the relevant headers before selecting a variant for IPC.

## Benchmark Claims, Platform Scope, and Upgrade Cost

The project links to dedicated throughput and latency benchmarks, but the README excerpt contains no benchmark values that can be reported as independent results. It does make performance-oriented claims, including a push-to-pop round trip that can be below 100 nanoseconds and an operating-system wake-up delay of roughly 1 to 3 microseconds. Those figures describe the project's stated operating context, not a guarantee for an application, machine, compiler, queue size, or message type.

Its emphasis on minimal instructions, cache placement, fixed storage, and no allocations can create a substantial compile and upgrade review cost. Changing capacity templates may affect stack or shared-memory allocation. Changing element types can affect copy or move behavior. Moving between queue variants requires checking their initialization and placement rules. Shared-memory deployments may also coordinate a layout between separately built processes, although the README does not explicitly promise a stable ABI.

The repository is not archived. Its last push was on 2026-10-16. The listed releases include `v1.9.4` from 2026-09-16 and `v1.9.3` from 2026-09-15. That recency shows recent repository activity, but upgrade cost still depends on `CHANGELOG.md`, constructor signatures, template names, and behavior changes between the version in use and the selected release.

The project uses the MIT License. It permits use, modification, distribution, and sublicensing subject to the license's conditions, including its notice requirements. This is not legal advice. Redistributors should retain the applicable copyright and license notice and review the full `LICENSE` file for their distribution model.

## Why the Fixed Ring Beats Other Queue Approaches for Some Workloads

The clearest alternative is a dynamically growing linked queue with an explicit memory-reclamation strategy. Its buffer can expand beyond an upfront capacity, so producers do not face the same fixed ring limit. The cost comes from managing node lifetime while consumers may still traverse them. atomic_queue avoids linked nodes and the associated reclamation problem by storing values in a contiguous ring and copying or moving them through queue operations.

A blocking queue based on `std::mutex` and a condition variable follows a different approach. It can wait for capacity or data, which is useful for ordinary producer-consumer workloads, but its waiting path introduces operating-system scheduling and wake-up costs. atomic_queue omits that waiting behavior so that active threads can hand off values through atomic operations without OS blocking.

Neither alternative wins under every set of requirements. The ring favors a bounded low-latency handoff. The linked queue favors flexible capacity but demands careful lifetime management. The blocking queue favors explicit backpressure but does not share atomic_queue's latency objective. A fixed-capacity ring is the wrong choice when rejecting a push is unacceptable and waiting for space is acceptable.

## Conclusion

Engineers building latency-sensitive C++ systems should consider atomic_queue when messages are small, copyable or movable, and bounded, especially for SPSC, MPMC, or shared-memory handoffs. Projects that require dynamic growth, blocking backpressure, durable buffering, or unbounded queues should use a different design. Before adoption, inspect the exact queue variant and API under `include/`, choose capacity for peak in-flight work, confirm that its nonblocking full and empty behavior matches the application, and review the MIT license against the project's distribution requirements. For this library, the final judgement depends on accepting fixed-capacity, nonblocking operation as part of the interface rather than an incidental configuration detail.

## FAQ

### Is atomic_queue suitable for C++14 applications?

Yes. The project targets C++14 and expects a platform with a working `std::atomic` implementation. Its primary development and benchmark environment is Linux x86_64, while the README also lists CI coverage for several other platforms and architectures.

### Does atomic_queue grow when the queue becomes full?

No. Its maximum size must be selected at compile time or construction time. The application needs to define what happens when a nonblocking push cannot place another element into the full ring.

### Can atomic_queue block until a producer or consumer is ready?

It provides no OS-blocking push or pop functions. This omission is deliberate because the project prioritizes low push-to-pop latency over operating-system wake-up behavior.

## Sources

- [License: MIT](https://github.com/max0x7ba/atomic_queue/blob/master/LICENSE)
- [max0x7ba/atomic_queue on GitHub](https://github.com/max0x7ba/atomic_queue)
- [Project website](https://max0x7ba.github.io/atomic_queue/html/benchmarks.html)
- [README](https://github.com/max0x7ba/atomic_queue/blob/master/README.md)
- [Releases](https://github.com/max0x7ba/atomic_queue/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/max0x7ba-atomic-queue
