Skip to main content

Module latest_buf

Module latest_buf 

Source
Expand description

Freshness-first SPSC snapshot channel.

LatestBuf retains at most one unread publication. A producer always publishes the newest complete T; if an older unread value is displaced, PublishReport::replaced_unread reports it. The consumer takes only the latest value and receives its generation plus the exact number skipped.

The channel uses three slots with exclusive ownership: one producer slot, one consumer slot, and one slot named by an atomic exchange state. An endpoint accesses a slot only after acquiring it through a single atomic swap. Producer and consumer therefore never touch the same payload bytes concurrently, unlike a seqlock.

Endpoint cursors live in the channel rather than the handles. Handles are stateless and dropping/reacquiring one continues its slot ownership, generation sequence, and skipped accounting.

An empty consumer poll first Acquire-loads the ready bit and returns without an atomic read-modify-write. A pending poll still performs the same AcqRel swap that transfers slot ownership. The private initial role indices are encoded as zero so a const-initialized channel lands in .bss rather than carrying its three payload slots in the flash-backed .data image.

§Decision status

Decision D1 (wrap-ambiguity policy) is closed as documented approximation plus payload escape hatch (contract §9, non-promise X6): skipped counts are exact while fewer than u32::MAX non-zero generations separate successful takes, and beyond that full wrap span the u32 result is a documented under-count — LatestItem::skipped carries the full disclosure.

Decision D2 (Source<T> policy) is closed: Consumer does not implement crate::Source, because try_pop cannot report the displacement that is this channel’s designed overload behaviour — crate::LatestSource is the consumer’s designed contract surface (contract §9, non-promise X7), and the absent impl is pinned by a compile_fail doctest on Consumer.

Decision D3 (first deliverable form) is closed: T stays generic by decision, not default — a complete block is a payload (LatestBuf<Block<T, N>> via the BlockBuf composition), sample-versus-block is release scheduling and RAM, and no separate latest-block type exists (contract §9).

Review caveat A.3 (handle-state continuation) is closed the same way this implementation works: role state is channel-resident in role-owned storage and handles are stateless, so a drop-and-reacquire continues by construction (contract H4; proposal Appendix A.3). Contract non-promise X8 states the role-recovery boundary: the role is held until the handle is dropped, handle lifetime is an application property, and there is deliberately no out-of-band role reset.

Every LatestBuf decision (D1–D3, A.3) is closed; the contract §9 and proposal Appendix A.3 carry the records.

§Example

use ph_eventing::LatestBuf;

let channel = LatestBuf::<u32>::new();
let producer = channel.try_producer().expect("producer");
let consumer = channel.try_consumer().expect("consumer");

assert!(!producer.publish(10).replaced_unread);
assert!(producer.publish(20).replaced_unread);
let item = consumer.take_latest().expect("latest value");
assert_eq!((item.value, item.generation, item.skipped), (20, 2, 1));

Structs§

Consumer
Unique, stateless read handle for a LatestBuf.
LatestBuf
Three-slot freshness-first SPSC snapshot channel.
LatestItem
A value claimed from a LatestBuf.
Producer
Unique, stateless write handle for a LatestBuf.
PublishReport
Result of one successful latest-value publication.