lockfreequeues¶
Package Consolidated into lockfree
lockfreequeues is now maintained strictly as a zero-overhead compatibility wrapper around the unified lockfree library.
All active development, performance tuning, and modern features (CSP Channel[T] facade, cross-language C ABI, Apple Silicon cacheline optimizations, and benchmark results) live in canonical lockfree.
👉 Official Documentation & Guides: https://elijahr.github.io/lockfree
Lock-free queues for Nim, implemented as ring buffers (bounded) and linked segments (unbounded).
Overview¶
Bounded Queues (Fixed Capacity)¶
Ring buffer implementations with compile-time capacity. Best for predictable memory usage and embedded systems.
| Queue | Producers | Consumers | Push | Pop |
|---|---|---|---|---|
| Sipsic | Single | Single | Wait-free | Wait-free |
| Sipmuc | Single | Multiple | Wait-free | Lock-free |
| Mupsic | Multiple | Single | Lock-free | Wait-free |
| Mupmuc | Multiple | Multiple | Lock-free | Lock-free |
Unbounded Queues (Dynamic Capacity)¶
Linked segment implementations that grow as needed. The MP/MC variants use DEBRA / NEBR epoch-based reclamation (via canonical lockfree) for safe memory deallocation; the SPSC variant frees retired segments inline (no manager).
| Queue | Producers | Consumers | Push | Pop |
|---|---|---|---|---|
| UnboundedSipsic | Single | Single | Wait-free | Wait-free |
| UnboundedSipmuc | Single | Multiple | Wait-free | Lock-free |
| UnboundedMupsic | Multiple | Single | Lock-free | Wait-free |
| UnboundedMupmuc | Multiple | Multiple | Lock-free | Lock-free |
Installation¶
Quick Start¶
Bounded Queue¶
import lockfreequeues
# Single-producer, single-consumer queue with capacity 16
var queue = initSipsic[16, int]()
queue.push(42)
queue.push(123)
let item = queue.pop() # some(42)
Unbounded Queue (single-producer, single-consumer)¶
UnboundedSipsic does not need DEBRA reclamation: the producer and consumer
each hold their own segment pointer and the consumer-side advance is the only
freer.
import lockfreequeues
# Unbounded SPSC queue with segment size 64
var queue = newUnboundedSipsic[64, int]()
queue.push(42) # Never fails - grows as needed
let item = queue.pop() # some(42)
Unbounded Queue (multi-producer, multi-consumer)¶
The MP/MC unbounded variants need a DebraManager for safe segment
reclamation, plus a per-thread handle for each producer and consumer:
import options
import debra
import lockfreequeues
var manager = initDebraManager[4]()
var queue = newUnboundedMupmuc[64, int, 4](addr manager)
let producerHandle = registerThread(manager)
let consumerHandle = registerThread(manager)
var producer = queue.getProducer(producerHandle)
var consumer = queue.getConsumer(consumerHandle)
producer.push(42)
let item = consumer.pop() # some(42)
Choosing a Queue¶
Use bounded queues when:
- Memory usage must be predictable
- Working in embedded or real-time systems
- Producer/consumer counts are known at compile time
Use unbounded queues when:
- Workload is bursty or unpredictable
- Producer/consumer threads are created dynamically
- Memory growth is acceptable
Examples¶
Examples are in the examples directory:
References¶
- Juho Snellman's "I've been writing ring buffers wrong all these years"
- Mamy Ratsimbazafy's research on SPSC channels
- Henrique F Bucher's "Yes, You Have Been Writing SPSC Queues Wrong Your Entire Life"