SWMR-Cell: Version-Based Single-Writer Multi-Reader Cell
swmr-cell provides a thread-safe SwmrCell that supports concurrent wait-free reads and lock-free writes (amortized) using version-based garbage collection. It is designed for high-performance scenarios where a single writer updates data frequently while multiple readers access it concurrently.
Features
- Single-Writer Multi-Reader (SWMR): Optimized for one writer thread and multiple concurrent reader threads.
- Wait-Free Reads: Readers obtain data snapshots without locking, blocking, or interfering with the writer.
- Version-Based Garbage Collection: Old data is automatically reclaimed when no readers are observing it, using a mechanism similar to RCU (Read-Copy-Update) or Epoch-based GC.
- Snapshot Semantics: A reader holds a consistent view of the data as long as the
PinGuardis alive. - Minimal Synchronization:
- Readers use atomic operations to register their active version.
- The writer uses a mutex only for managing reader registration and scanning reader states during collection.
- Configurable GC: Supports automatic or manual garbage collection with tunable thresholds.
- Type-Safe: Full Rust ownership and lifetime safety.
Usage
Add swmr-cell to your Cargo.toml:
[]
= "0.1"
Basic Example
use SwmrCell;
// 1. Create a new SWMR cell with an initial value
let mut cell = new;
// 2. Create a local reader for this thread (each thread needs its own local reader)
let local = cell.local;
// 3. Pin and read the value
// The guard provides a snapshot of the value at the moment of pinning
let guard = local.pin;
assert_eq!;
drop; // Reader is no longer accessing the data
// 4. Writer updates the value
// This creates a new version; old version is retired
cell.store;
// 5. Read the new value
let guard = local.pin;
assert_eq!;
Threaded Example
use SwmrCell;
use Arc;
use thread;
Using SwmrReader for Shared Reader Creation
If you need to distribute the ability to create readers to multiple threads (e.g., in a thread pool where threads are dynamic), you can use SwmrReader. Unlike LocalReader, SwmrReader is Sync and Clone.
use SwmrCell;
use thread;
let mut cell = new;
// Create a SwmrReader factory that can be shared
let reader_factory = cell.reader;
for i in 0..3
Configuration
You can customize the garbage collection behavior using SwmrCell::builder():
use SwmrCell;
let mut cell = builder
.auto_reclaim_threshold // Trigger GC automatically after 128 stores (default: 16)
// .auto_reclaim_threshold(None) // Disable automatic GC
.build;
- auto_reclaim_threshold: Controls how many retired objects are buffered before scanning readers for reclamation. Larger values reduce GC overhead but increase memory usage.
How It Works
- Versions: The cell maintains a global monotonic version counter. Each
storeincrements the version. - Readers: Each
LocalReaderhas a slot where it publishes the version it is currently accessing (active_version). - Reclamation: When the writer updates the value, the old value is added to a garbage queue with the version it belonged to.
- Collection: The writer scans all active reader slots. It calculates the minimum version currently visible to any reader. Any garbage from versions strictly older than this minimum is safe to reclaim.
Safety
LocalReaderis!Syncand must remain on the thread that uses it.PinGuardis bound to the lifetime of theLocalReaderand prevents the underlying data from being freed while held.- Accessing the data via
PinGuardis safe because the writer guarantees that no version visible to an active reader will be deallocated.
License
This project is licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.