Expand description
SharedRing<P> - cross-thread / cross-process lock-free MPMC ring
backed by a memory-mapped file.
One mechanism gives you THREE deployment modes:
- Cross-thread: multiple threads in one process map the same file; lock-free CAS handles concurrency.
- Cross-process: multiple processes open the same file via
SharedRing::open; the OS page-cache aliases them onto the same physical pages. - Disk-persistent: the MMF is backed by a real file; the
kernel writes dirty pages to disk on its own schedule, plus
SharedRing::flushforces a sync when the caller wants durability.
The same byte layout serves all three.
§Layout
+-----------------------------+
| RingHeader (64B aligned) | producer_seq, consumer_seq,
| | capacity, slot_size, magic
+-----------------------------+
| Slot[0] (64B cache line) | state + sequence + payload
| Slot[1] |
| ... |
| Slot[capacity - 1] |
+-----------------------------+Each slot is exactly one cache line (64 bytes). The state field advances through EMPTY -> CLAIMED_BY_PRODUCER -> PUBLISHED -> CLAIMED_BY_CONSUMER -> EMPTY in a closed loop.
§Concurrency protocol
Producers:
- Read
producer_seq(atomic). - Compute
slot_idx = producer_seq % capacity. - Read slot’s sequence number; if it doesn’t equal
producer_seq, the ring is full (slot still holds an unconsumed value). Retry or fail. - CAS
producer_seqfrom S to S+1. On success, the slot is ours to write; copy payload, then store slot.sequence = S+1 (release).
Consumers:
- Read
consumer_seq. slot_idx = consumer_seq % capacity.- Acquire-load slot.sequence; must equal
consumer_seq + 1(means producer published). Otherwise empty. - CAS
consumer_seqfrom S to S+1. On success, read payload, then store slot.sequence = S + capacity (releases the slot for the next producer that will use it at producer_seq = S + capacity).
This is the classic Vyukov MPMC bounded-queue protocol.
Structs§
- Consumer
- Sole-consumer handle on a
SharedRingSpscpair. SameSend + !Sync + !Cloneshape asProducer, mirroring the SPSC contract on the read side. - Lazy
Shared Ring - Defer the file-backed MMF setup until first use.
- Producer
- Sole-producer handle on a
SharedRingSpscpair.Sendso it can be moved to a producer thread;!Syncso it cannot be shared across threads (which would violate the SPSC contract). NotClone: a second producer is statically impossible. - Ring
Header - Header layout: three cache lines so the two hot counters never
false-share. Line 0 is read-mostly metadata (plus the
rarely-written
epoch);producer_seqandconsumer_seqeach get their own line. Every producer CASesproducer_seqand every consumer CASesconsumer_seq; co-locating them on one line made each side’s CAS invalidate the other side’s copy, serializing the producer and consumer coherence traffic under contention. The SPSC ring separateshead/tailfor exactly this reason. - Shared
Ring - Shared
Ring Spsc - Compile-time-enforced single-producer / single-consumer ring,
backed by the Lamport 1983 SPSC core in
crate::spsc_ring::SpscRingCore. - Slot
Enums§
Constants§
- PAYLOAD_
BYTES - Payload bytes per slot = SLOT_SIZE - sizeof(sequence: u64).
- RING_
MAGIC - Magic number to detect a valid ring header. ASCII ‘APMF’ + version.
- SLOT_
SIZE - Each slot is exactly one cache line.
Functions§
- ring_
file_ size - Compute the total MMF size for a ring of
capacityslots.