ph_eventing/lib.rs
1//! Eventing primitives for no-std embedded targets.
2//!
3//! # Highlights
4//! - Lock-free SPSC sequence ring for high-throughput telemetry.
5//! - No allocation, no dynamic dispatch.
6//! - Designed for fast producers and potentially slower consumers.
7//!
8//! # Quick start
9//! ```
10//! use ph_eventing::SeqRing;
11//!
12//! let ring = SeqRing::<u32, 64>::new();
13//! let producer = ring.producer();
14//! let mut consumer = ring.consumer();
15//!
16//! producer.push(42);
17//! consumer.poll_one(|seq, v| {
18//! assert_eq!(seq, 1);
19//! assert_eq!(*v, 42);
20//! });
21//! ```
22//!
23//! # No-std
24//! The crate is `#![no_std]` by default. Tests require `std`.
25//!
26//! # Targets without atomics
27//! For targets that lack 32-bit atomics (for example `thumbv6m-none-eabi`), enable
28//! `portable-atomic-unsafe-assume-single-core` or `portable-atomic-critical-section`.
29//!
30//! # Safety and concurrency
31//! This crate is SPSC by design: exactly one producer and one consumer must be active.
32//! `producer()`/`consumer()` will panic if called while another handle of the same kind is active.
33//! Using unsafe to bypass these constraints (or sharing handles concurrently) is undefined behavior.
34//!
35//! # Semantics
36//! - Sequence numbers are monotonically increasing `u32` values; `0` is reserved for "empty".
37//! - `poll_one`/`poll_up_to` drain in-order and return `PollStats`.
38//! - `latest` reads the newest value without advancing the consumer cursor.
39//! - If the consumer lags by more than `N`, it skips ahead and reports drops via `PollStats`.
40#![no_std]
41
42#[cfg(all(not(target_has_atomic = "32"), not(feature = "portable-atomic")))]
43compile_error!(
44 "ph-eventing requires 32-bit atomics. For thumbv6m and other no-atomic targets, \
45enable either the portable-atomic-unsafe-assume-single-core or portable-atomic-critical-section feature."
46);
47
48pub mod seq_ring;
49
50pub use seq_ring::{Consumer, PollStats, Producer, SeqRing};
51
52#[cfg(test)]
53extern crate std;