Skip to main content

CapacityAdaptiveRing

Struct CapacityAdaptiveRing 

Source
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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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).

Source

pub fn register_consumer(&self) -> Result<usize, AdaptiveError>

Register a consumer on the active backing. Same lifetime caveat as register_producer.

Source

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.

Source

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.

Source

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.

Source

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 BackingTarget for this morph AND every subsequent morph / prewarm.
Source

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.

Source

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.

Source

pub fn warm_capacity(&self) -> Option<usize>

Capacity currently held in the warm cache, if any.

Source

pub fn warm_hits(&self) -> u64

Number of morphs that consumed a warm-cache prediction.

Source

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.

Source

pub fn clear_warm(&self)

Drop any cached prediction, releasing its memory (and its file / shm region for non-anon locales).

Source

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.

Source

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.

Source

pub fn is_stamped(&self) -> bool

Whether this wrapper’s backings carry ordering stamps.

Source

pub fn ordering_mode(&self) -> Option<OrderingMode>

Live ordering mode of the active backing (None when unstamped).

Source

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).

Source

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.

Trait Implementations§

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.