pub struct CapacityAdaptiveRing { /* private fields */ }Expand description
Runtime-resizable adaptive ring.
See the module-level docs for the morph protocol. Hot-path
try_send / try_recv calls hop through one ArcSwap load plus
the active AdaptiveRing’s dispatch.
Implementations§
Source§impl CapacityAdaptiveRing
impl CapacityAdaptiveRing
Sourcepub fn create_anon(
max_producers: usize,
max_consumers: usize,
initial_capacity: usize,
) -> Result<Self, CapacityMorphError>
pub fn create_anon( max_producers: usize, max_consumers: usize, initial_capacity: usize, ) -> Result<Self, CapacityMorphError>
Anon (in-process) capacity-adaptive ring. The active backing is an anonymous mmap; subsequent morphs allocate fresh anon mmaps at the new capacity and drop the prior one once stragglers drain.
Sourcepub fn create_anon_stamped(
max_producers: usize,
max_consumers: usize,
initial_capacity: usize,
) -> Result<Self, CapacityMorphError>
pub fn create_anon_stamped( max_producers: usize, max_consumers: usize, initial_capacity: usize, ) -> Result<Self, CapacityMorphError>
As create_anon with ordering stamps
on the backing (and on every backing subsequent capacity
morphs allocate). See
AdaptiveRing::with_ordering_stamps.
Sourcepub fn create(
base_path: impl AsRef<Path>,
max_producers: usize,
max_consumers: usize,
initial_capacity: usize,
) -> Result<Self, CapacityMorphError>
pub fn create( base_path: impl AsRef<Path>, max_producers: usize, max_consumers: usize, initial_capacity: usize, ) -> Result<Self, CapacityMorphError>
File-backed capacity-adaptive ring. The active backing is
{base_path}.cap_{initial_capacity}.bin; morphs allocate
fresh files at the morph target’s suffix and drop the prior
file once stragglers drain.
Sourcepub fn create_stamped(
base_path: impl AsRef<Path>,
max_producers: usize,
max_consumers: usize,
initial_capacity: usize,
) -> Result<Self, CapacityMorphError>
pub fn create_stamped( base_path: impl AsRef<Path>, max_producers: usize, max_consumers: usize, initial_capacity: usize, ) -> Result<Self, CapacityMorphError>
As create with ordering stamps on the
backing and every morph-allocated successor.
Sourcepub fn create_shmfs(
name_prefix: &str,
max_producers: usize,
max_consumers: usize,
initial_capacity: usize,
) -> Result<Self, CapacityMorphError>
pub fn create_shmfs( name_prefix: &str, max_producers: usize, max_consumers: usize, initial_capacity: usize, ) -> Result<Self, CapacityMorphError>
ShmFs (named shared memory) capacity-adaptive ring. The
active backing is named {name_prefix}_cap_{initial_capacity};
morphs allocate fresh named-shm regions at the morph
target’s suffix and drop the prior region once stragglers
drain. Cross-process visible: another process opens the
same logical ring by constructing a CapacityAdaptiveRing
with the same name_prefix.
Sourcepub fn create_shmfs_stamped(
name_prefix: &str,
max_producers: usize,
max_consumers: usize,
initial_capacity: usize,
) -> Result<Self, CapacityMorphError>
pub fn create_shmfs_stamped( name_prefix: &str, max_producers: usize, max_consumers: usize, initial_capacity: usize, ) -> Result<Self, CapacityMorphError>
As create_shmfs with ordering stamps
on the backing and every morph-allocated successor.
Sourcepub fn current_capacity(&self) -> usize
pub fn current_capacity(&self) -> usize
Current capacity of the active backing. Stays in lockstep with the active ArcSwap; observers see the value the morph publishes via a Release store.
Sourcepub fn pin_generation(&self) -> u64
pub fn pin_generation(&self) -> u64
Current pin generation. Pinned handles capture this at pin time; a different live value means the pin is stale.
Sourcepub fn register_producer(&self) -> Result<usize, AdaptiveError>
pub fn register_producer(&self) -> Result<usize, AdaptiveError>
Register a producer on the active backing. The returned id
is valid only against the current capacity backing; after a
morph the caller re-registers against the new active backing
(mirrored automatically by morph_capacity_to).
Sourcepub fn register_consumer(&self) -> Result<usize, AdaptiveError>
pub fn register_consumer(&self) -> Result<usize, AdaptiveError>
Register a consumer on the active backing. Same lifetime
caveat as register_producer.
Sourcepub fn try_send(
&self,
producer_id: usize,
payload: &[u8],
) -> Result<(), RingError>
pub fn try_send( &self, producer_id: usize, payload: &[u8], ) -> Result<(), RingError>
Hot-path push. One ArcSwap load + the active backing’s
dispatched try_send.
Sourcepub fn try_recv(
&self,
consumer_id: usize,
out: &mut [u8],
) -> Result<usize, RingError>
pub fn try_recv( &self, consumer_id: usize, out: &mut [u8], ) -> Result<usize, RingError>
Hot-path pop. Walks the stale backing list oldest-first, returning the first non-empty backing’s item; falls through to the active backing when every stale entry is empty.
The consumer is the SOLE reader of every backing (stale +
active). Producers only ever write to active. This is what
preserves the SPSC contract on the per-backing
SpscRingCore: exactly one consumer touches it, even
across morph boundaries.
FIFO ordering invariant: stale and active are captured under the SAME mutex acquisition (the stale lock). This prevents the race where a morph slips in between the stale-snapshot and the active-load and the consumer ends up reading from the new active while the old active sits in the new stale tail unread - which would reorder items the producer pushed to the soon-to-be-stale ring AFTER items the producer pushed to the brand-new active.
Sourcepub fn morph_capacity_to(
&self,
new_capacity: usize,
) -> Result<(), CapacityMorphError>
pub fn morph_capacity_to( &self, new_capacity: usize, ) -> Result<(), CapacityMorphError>
Morph the ring’s capacity to new_capacity. Allocates a
fresh backing at the new size, bumps pin_generation,
stashes the old backing onto the stale list (the
consumer drains it via try_recv’s stale-walk), and
atomic-swaps the active pointer. Concurrent morphs are
serialised through an internal mutex; hot-path ops are not
blocked.
Critically the morph DOES NOT drain the old backing - that
would race against the consumer’s concurrent try_recv on
the same backing, violating the per-backing
SPSC/MPSC/MPMC contract (two consumers on an SPSC ring is
undefined behavior). Instead the old backing stays
reachable via the stale list; the consumer is the sole
reader and pops every in-flight item via try_recv’s
stale-walk-then-active pattern.
Shrink always succeeds. In-flight items physically remain in the old (larger) backing as part of the stale list; the new capacity governs only items the producer pushes after the morph. Memory holds both old + new backings until the consumer drains old, at which point the next morph prunes the empty old entry from the stale list.
Sourcepub fn morph_to_config(
&self,
target: &RingConfig,
) -> Result<(), CapacityMorphError>
pub fn morph_to_config( &self, target: &RingConfig, ) -> Result<(), CapacityMorphError>
Compound morph: change any subset of {shape, capacity,
locale} in ONE transition. Builds a single fresh backing at
the combined target (warm-cache hit when
prewarm_config predicted it),
seeds stamps, mirrors registrations once, applies the
target shape to the empty new backing, bumps the pin
generation once, and appends the displaced active to the
stale list once - however many axes changed. A sequential
walk of the same axes pays each of those costs per axis.
Special cases:
- Every axis already at target: no-op, no generation bump.
- Shape-only change (capacity + locale unchanged):
delegates to the active backing’s in-place shape morph
(all four shape protocols are pre-allocated inside
AdaptiveRing), so no fresh backing is built, the wrapper pin stays valid, and in-flight items stay put. - A locale axis retargets the wrapper’s
BackingTargetfor this morph AND every subsequent morph / prewarm.
Sourcepub fn prewarm(&self, capacity: usize) -> Result<(), CapacityMorphError>
pub fn prewarm(&self, capacity: usize) -> Result<(), CapacityMorphError>
Speculatively build a backing at capacity (current
locale) into the one-slot warm cache, off the morph lock’s
critical path. The next morph targeting that capacity
consumes it and skips allocation + mapping + zeroing.
Sourcepub fn prewarm_config(
&self,
target: &RingConfig,
) -> Result<(), CapacityMorphError>
pub fn prewarm_config( &self, target: &RingConfig, ) -> Result<(), CapacityMorphError>
Speculatively build a backing at target’s (capacity,
locale) into the one-slot warm cache, off the morph lock’s
critical path - the build half of a build-beside-and-
repatch transition: the following
morph_to_config at the same
target consumes it and pays only the swap. The shape axis
is ignored here: the swap path shapes the empty backing in
microseconds. Replaces any previously cached prediction
(the slot holds exactly one); re-prewarming the cached
(capacity, locale) is a no-op.
Sourcepub fn warm_capacity(&self) -> Option<usize>
pub fn warm_capacity(&self) -> Option<usize>
Capacity currently held in the warm cache, if any.
Sourcepub fn stale_pops(&self) -> u64
pub fn stale_pops(&self) -> u64
Items the consumer popped from stale (post-morph) backings
rather than the active one, since construction. The
transition-cost observability counterpart to warm_hits.
Sourcepub fn clear_warm(&self)
pub fn clear_warm(&self)
Drop any cached prediction, releasing its memory (and its file / shm region for non-anon locales).
Sourcepub fn pin_current_capacity(&self) -> PinnedCapacity<'_>
pub fn pin_current_capacity(&self) -> PinnedCapacity<'_>
Pin the current capacity backing for a hot loop. The
returned PinnedCapacity exposes the underlying
AdaptiveRing directly and validates
against the pin generation via
is_still_valid.
Sourcepub fn ring_handle(&self) -> Arc<AdaptiveRing> ⓘ
pub fn ring_handle(&self) -> Arc<AdaptiveRing> ⓘ
Direct access to the active AdaptiveRing.
Override hatch for callers that want the shape-axis surface
on top of the capacity-axis morphing.
Sourcepub fn is_stamped(&self) -> bool
pub fn is_stamped(&self) -> bool
Whether this wrapper’s backings carry ordering stamps.
Sourcepub fn ordering_mode(&self) -> Option<OrderingMode>
pub fn ordering_mode(&self) -> Option<OrderingMode>
Live ordering mode of the active backing (None when
unstamped).
Sourcepub fn set_ordering_mode(&self, mode: OrderingMode) -> Result<(), RingError>
pub fn set_ordering_mode(&self, mode: OrderingMode) -> Result<(), RingError>
Flip the ordering mode across the active backing AND every stale backing still draining, so the consumer’s stale-walk-then-active pop applies one consistent discipline. Cross-backing order note: producers only ever write to the active backing, so every stale item predates every active item - the stale-oldest-first walk composes with per-backing stamp merging into global stamp order across the morph boundary (within the stamp source’s skew window).
Sourcepub fn inversions(&self) -> u64
pub fn inversions(&self) -> u64
Cross-producer inversions observed on the active backing. Continuous across capacity morphs: each morph seeds the fresh region’s counter from the old one.