All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Auto-create constructors for unbounded MP/SP variants.
newUnboundedMupmuc[S, T, MaxThreads](strategy),newUnboundedSipmuc[S, T, MaxThreads](strategy), andnewUnboundedMupsic[S, T, MaxThreads](strategy)(the last auto-registers the caller as the consumer). Each heap-allocates a privateDebraManagerowned by the queue; teardown happens inside the queue's=destroyafter segment cleanup. The existing explicit-manager API (addr manager) is preserved for multi-queue setups that share a manager. - Auto-register
getProducer()/getConsumer()overloads. No-arg variants that callregisterThreadinternally. Each call consumes one thread slot; threads using multiple queues with a shared manager should prefer the explicit-handle overloads. - Bidirectional client refcount on
DebraManager(vianim-debra >= 0.5.0). Queue constructors callbindClient;=destroycallsunbindClient. The manager's destructor assertsclientCount == 0, catching the case where a shared manager is destroyed before its queues.
- Bump minimum
debrato 0.5.0. - Bump minimum
typestatesto 0.6.0. src/lockfreequeues/atomic_dsl.nimno longer defines a localcompareExchangeshim — it's now provided bydebra/atomics.
README.md"Thread safety" section rewritten with the correct explanation of whyrefitems are rejected (=copy/=sinkhooks race on slot refcounts in the sharedarray[S, T]), replacing the prior incorrect spinlock claim.- "Choosing a queue" table split into separate Bounded and Unbounded tables for better rendering.
- The same
{.error.}strings insideunbounded_*.nimwere updated to match.
docs/api/epoch.mdremoved (referenced a module extracted intonim-debra); was breaking themkdocs buildstep in the docs deploy workflow..github/workflows/docs.ymltriggers extended to includedevelbranch andworkflow_dispatch.
- Bounded MPMC/SPMC/MPSC slot protocol switched to per-slot sequence counters (Vyukov bounded-MPMC). Fixes a confirmed race that allowed two consumers to claim the same physical slot across generations, producing silent duplicate-item delivery and producer-vs-producer storage races. The race was TSAN-confirmed at 100% reproduction and ran at roughly a 5% release-mode duplicate rate under contention.
Mupmuc,Mupsic,Sipmuctypes: thecommitted*,reservedHead*,reservedTail*, andstorage*fields have been removed. They are replaced withcells*: MPMCCellArrayN[N, T]. Consumers introspecting these fields directly must migrate to the new accessors.headandtailcursors on the bounded queue types are nowAtomic[uint64]instead ofAtomic[int]. Code reading these via.load(...)will need an explicit cast or a local rename.- Bulk
push(items)/pop(count)semantics have changed. The previous implementation performed an atomic block-claim across the requested range; the new implementation performs a best-effort fill via a loop of singleton operations. Partial completion is reported through the existingOption[Slice[int]]/Option[seq[T]]return types, so the API surface is unchanged but the intra-call atomicity guarantee is gone. CommittedFlagsNtype removed. Replaced withSlotSeqN,MPMCCellPayload, andMPMCCellArrayN. Thetests/t_committed_flags_n.nimfile has been deleted; equivalent and stronger coverage lives intests/t_slot_seq_n.nim.
- New
lockfreequeues/backoffmodule withbackoffOnRetry(exponential) andbackoffOnPeerWait(cpuPause-only) helpers. Used internally on CAS-retry paths to handle CPU oversubscription without burning unbounded CPU. - Bench harness now supports the
Mupmuc8P/8C topology. The previous topology table was implicitly capped at 4P/4C. - New
LFQ_STRESS_DURATION_SECenvironment variable on threaded stress tests, for sustained-load runs beyond the default iteration budget. - New
tests/t_slot_seq_generation_rollover.nim: a deterministic single-threaded reproduction of the original protocol-bug scenario, asserting that the new sequence-counter protocol rejects the stale second-consumer claim. - Throughput bench harness for
UnboundedMupsic(1P/1C, 2P/1C, 4P/1C) atbenchmarks/nim/bench_throughput.nimplus a thin adapter atbenchmarks/nim/adapters/lockfreequeues_unbounded_mupsic.nimthat owns the queue andDebraManager. Producer threads register their ownThreadHandlein-thread (handles are per-thread by construction). The new variants run for 33 timed iterations + 3 warmup; existing Sipsic/Mupmuc/Channels run counts are unchanged. bench_throughputaccepts variant-group args (sipsic,mupmuc,unbounded_mupsic,channels) to limit which benchmarks run. With no args, all variants run (backward compatible). Multiple args take the union of groups. Unknown args print the supported list and exit non-zero. Lets gate runs target a single queue family without paying for the slow bounded MPMC variants.- Compile-time overrides for
bench_throughputrun shape via{.intdefine.}constants:-d:MessageCount=N,-d:DefaultRuns=N,-d:WarmupRuns=N,-d:UnboundedMupsicRuns=N,-d:UnboundedMupsicSegmentSize=N,-d:UnboundedMupsicMaxThreads=N. Defaults are unchanged (1M messages, 33 runs, 3 warmup). Lets gate runs trade statistical confidence for wall-clock budget without source edits.bench_throughputalso unbuffers stdout inisMainModuleso progress is visible under file redirect.
- Mupmuc 4P/4C livelock under CPU oversubscription. The combination of new backoff helpers and monotonic per-thread retry counters resolves the scheduler-pressure livelock; the bounded queue can now run 8 contending threads on 4 vCPUs without hangs.
- Bench harness
messageCount div Ptruncation bug. Consumers waited forever for items the integer-division truncation had silently discarded. Spread-the-remainder fix applied in three places. - Several pre-existing breakages in the unbounded threaded stress tests. The 3.2.0 DEBRA migration left them on a deleted
EpochManagerAPI; the tests have been updated to the current handle-based API. A small number remain disabled pending separate cleanup.
- Bounded queue documentation updated to reflect the new sequence-counter publication protocol. Unbounded queue documentation now explicitly disambiguates the segment-local committed-flag protocol from the bounded sequence-counter protocol. See
docs/safety-model.mdanddocs/slot-ownership-typestates.md.
- New queue types:
Sipmuc: bounded single-producer, multi-consumer queue.UnboundedSipsic: segmented unbounded single-producer, single-consumer queue (no reclamation needed).UnboundedSipmuc: segmented unbounded single-producer, multi-consumer queue with DEBRA reclamation.UnboundedMupsic: segmented unbounded multi-producer, single-consumer queue with DEBRA reclamation.UnboundedMupmuc: segmented unbounded multi-producer, multi-consumer queue with DEBRA reclamation.- Segment storage uses libc
c_calloc/c_free(viasystem/ansi_c); a nil return fromc_callocraisesOutOfMemDefect. Avoids the cross-thread free hazard from Nim'sallocShared, which routes through per-thread heap metadata. - The consumer-visible head pointer is
Atomic[ptr Segment]and is CAS-advanced past exhausted segments; the CAS winner retires the old segment via DEBRA.
- Typestate-driven push and pop modules under
src/lockfreequeues/typestates/for both bounded and unbounded queues. The high-level queue APIs now build on these typestate transitions. DeallocationStrategy(Manual/Eager) on the unbounded queues, configured at queue construction.Eagerretires and immediately attempts reclamation per pop;Manualaccumulates retired segments for an externaltryReclaimcall. Default isEager, exceptManualunder--gc:none.- Compile-time
-d:LockFreeQueuesAdvanceEvery=N(default 64) to tune the per-pop epoch-advance cadence in the unbounded queue retirement paths. - Compile-time lock-free check for queue item types: arc/orc compilation errors when a queue holds
refitems (which fall back to spinlock refcounting on those memory managers). Opt out with-d:allowNonLockFreeQueueItems. - Threaded reclamation tests for all four unbounded queue variants (
t_unbounded_*_threaded), exercised under arc, orc, and refc, plus the TSAN and ASAN sanitizer matrix. - Latency and throughput benchmark suite under
benchmarks/nim/(bench_latency.nim,bench_throughput.nim,bench_main.nim) with adapters for each queue type. - New examples:
audio_buffer.nim,event_collector.nim,job_scheduler.nim,task_fanout.nim, andsipmuc.nim. - Thread safety section and slot-ownership typestate documentation in README.
- CI matrix across arc, orc, and refc memory managers, including a
-d:nimEnforceLockFreeAtomicslane. - Dependency on
debra >= 0.3.0for safe memory reclamation in the unbounded multi-consumer queues. - Dependency on
typestates >= 0.3.1(already used; bumped to pull in the latest API).
- Eager-strategy unbounded queues now gate
reclaimNowonadvanceEveryreturningtrue, eliminating per-pop epoch-safety atomic loads when the global epoch hasn't advanced. Reclamation latency is bounded byLockFreeQueuesAdvanceEvery(default 64), the same cadence the user already controls. - Bounded queues (
Sipsic,Mupsic,Mupmuc) reimplemented on the typestate layer. SPSC uses N+1 storage slots to distinguish empty from full; MPSC, SPMC, and MPMC use N storage slots paired with per-slot committed flags so producers can publish before consumers observe the slot. Surface API (push/pop,head/tail, capacity semantics) is unchanged for SPSC; the multi-producer / multi-consumer variants gain a published-before-visible ordering guarantee they did not previously provide. atomic_dsl.nimnow re-exportsdebra/atomicsinstead of wrappingstd/atomics. Call-site DSL (relaxed,acquire,release,sequential) is unchanged.- Stress test runner exercises all three memory managers.
std/atomicsdependency.Atomic[T]and the memory-order primitives are now sourced fromdebra/atomics.src/lockfreequeues/constants.nim.CacheLineBytesis now sourced fromdebra/atomics.- Removed the internal
lockfreequeues/opssubmodule. It was documented as internal, had no callers inside the library, and itsindexhelper had silently shifted fromvalue mod capacity(3.1.0) tovalue mod (capacity + 1)during the queue refactor. External code that importedlockfreequeues/opsdirectly should migrate to the public typestate API or inline the small helpers it contained.
- Fixed wraparound issue in
full() - Drop support for Nim v1 due to compilation issue with atomics.
- README link to Gitter chat room.
- Regenerate documentation on PR merge.
- Test against Nim 1.6.0.
- Convert
NoConsumersAvailableDefectandNoProducersAvailableDefecttoCatchableErrors; there might be some value in catching them.
- Use correct memory orderings, as reported in #6
- Move changelog from README.md to CHANGELOG.md
- Fix issue with htmldocs submodule during
nimble install lockfreequeues.
- Moved from Travis CI to GitHub Actions.
- Multi-producer, single-consumer queue (Mupsic)
- Multi-producer, multi-consumer queue (Mupmuc)
- Nicer examples
- Refactor
- Fix wrap-around bug, improve test coverage
- Shared memory queues
- Addresses feedback from #1
headandtailare now in the range0 ..<2*capacitycapacitydoesn’t have to be a power of two- Use
alignpragma instead of padding array
- Initial release, containing
SipsicSharedQueueandSipsicStaticQueue