nexus-slot
High-performance conflation slots for "latest value wins" scenarios.
Overview
nexus-slot provides single-value slots optimized for the common pattern where only the most recent value matters: market data snapshots, sensor readings, configuration updates, position state, etc.
- Writer overwrites with newest data (never blocks)
- Reader gets each new value exactly once
- Old values are silently discarded (conflated)
Two variants based on reader topology:
| Variant | Readers | Overhead |
|---|---|---|
[spsc] |
Single reader | Lowest — disconnect via refcount |
[spmc] |
Multiple readers (Clone) |
+1 AtomicBool for writer-dropped flag |
Performance
Benchmarked on Intel Core Ultra 7 155H, pinned to physical cores with turbo disabled:
| Implementation | p50 Latency | Notes |
|---|---|---|
| nexus-slot | 159 cycles (59 ns) | SPSC only |
seqlock crate |
355 cycles (132 ns) | Supports multiple writers |
ArrayQueue(1) |
540 cycles (201 ns) | General-purpose bounded queue |
The 2.2x speedup over seqlock comes from specializing for single-producer:
- No writer contention → no CAS loops
- Cached sequence number → writer avoids atomic load
- No queue machinery → just a sequence counter
Usage
SPSC — Single Reader
use spsc;
let = ;
// Writer side (e.g., market data thread)
writer.write;
writer.write; // Overwrites previous
// Reader side (e.g., trading logic thread)
let quote = reader.read; // Gets sequence 2, skipped 1
assert_eq!;
// Already consumed - returns None until next write
assert!;
SPMC — Multiple Readers
use spmc;
let = ;
let mut reader2 = reader1.clone;
writer.write;
// Each reader consumes independently
assert_eq!;
assert_eq!;
// Both consumed
assert!;
assert!;
Semantics
Conflation
Multiple writes before a read result in only the latest value being observed:
writer.write(value_1);
writer.write(value_2);
writer.write(value_3);
// Reader only sees value_3
assert_eq!(reader.read(), Some(value_3));
assert!(reader.read().is_none());
Exactly-Once Delivery
Each written value can be read at most once per reader:
writer.write(value);
assert!(reader.read().is_some()); // Consumes it
assert!(reader.read().is_none()); // Already consumed
Check Without Consuming
if reader.has_update() {
// New data available, but not consumed yet
let value = reader.read(); // Now consumed
}
The Pod Trait
Types must implement Pod (Plain Old Data) — no heap allocations or drop glue.
Any Copy type automatically implements Pod:
use slot;
// These work automatically
let = ;
let = ;
let = ;
For non-Copy types that are still just bytes:
use Pod;
// SAFETY: OrderBook is just bytes, no heap allocations
unsafe
let = ;
Pod requirements:
- No
Vec,String,Box,Arc, or other heap types - No
File,TcpStream,Mutex, or other resources std::mem::needs_drop::<T>()must befalse
API
SPSC
| Type | Method | Description |
|---|---|---|
Writer |
write(value) |
Overwrite with new value (never blocks) |
Writer |
is_disconnected() |
Returns true if reader was dropped |
Reader |
read() -> Option<T> |
Get latest value if new, consuming it |
Reader |
has_update() -> bool |
Check for new data without consuming |
Reader |
is_disconnected() |
Returns true if writer was dropped |
SPMC
| Type | Method | Description |
|---|---|---|
Writer |
write(value) |
Overwrite with new value (never blocks) |
Writer |
is_disconnected() |
Returns true if all readers were dropped |
SharedReader |
read() -> Option<T> |
Get latest value if new, consuming it |
SharedReader |
has_update() -> bool |
Check for new data without consuming |
SharedReader |
is_disconnected() |
Returns true if writer was dropped |
SharedReader |
clone() |
Create independent reader at same position |
Implementation
The sequence counter starts at 2 (not 0) to provide 32-bit wrap protection. Starting at 0 would mean a full 32-bit wrap lands back on 0, which is indistinguishable from "no writes have occurred." Starting at 2 shifts the wrap point away from the initial state.
Uses a sequence lock (seqlock) internally:
- Writer increments sequence to odd (write in progress)
- Writer copies data via word-at-a-time atomic stores (
Relaxed) - Writer increments sequence to even (write complete)
- Reader loads sequence, copies data via word-at-a-time atomic loads, checks sequence unchanged
- If sequence changed during read, retry
The single-producer constraint allows caching the sequence on the writer side, eliminating an atomic load per write. Standalone Release/Acquire fences provide ordering.
Thread Safety
| Type | Send |
Sync |
Notes |
|---|---|---|---|
spsc::Writer<T> |
Yes | No | One thread only |
spsc::Reader<T> |
Yes | No | One thread only |
spmc::Writer<T> |
Yes | No | One thread only |
spmc::SharedReader<T> |
Yes | No | Clone for each thread |
License
MIT OR Apache-2.0