Skip to content

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

nimble install lockfreequeues

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:

nimble examples

References