Skip to content

Releases: userFRM/photon-ring

v2.5.0 — 7 cons eliminated

25 Mar 21:47

Choose a tag to compare

Highlights

  • Arbitrary capacity — any ring size >= 2 (Lemire fastmod, zero regression for pow2)
  • photon-ring-async — runtime-agnostic async wrappers (no tokio)
  • photon-ring-metrics — observability with snapshot/delta tracking
  • Pipeline WaitStrategythen_with(f, WaitStrategy::YieldSpin)
  • #[photon(as_enum)] — explicit enum marking, compile error for unknown types
  • Loom MPMC model — exhaustive concurrency testing (7 scenarios)

Install

photon-ring = "2.5.0"
photon-ring-async = "2.5.0"    # optional
photon-ring-metrics = "2.5.0"  # optional

Breaking

  • #[derive(Message)] enum fields now require #[photon(as_enum)] attribute

Full changelog: v2.4.0...v2.5.0

v2.4.0 — atomic-slots + multi-model audit

19 Mar 12:15

Choose a tag to compare

What's New

atomic-slots feature — formally sound seqlock

Replaces write_volatile/read_volatile with AtomicU64 stripes. Zero performance cost on x86-64 (identical MOV instructions). ~5-10ns ARM64 reader overhead. Miri-passable. First formally-sound seqlock implementation in the Rust ecosystem.

photon-ring = { version = "2.4.0", features = ["atomic-slots"] }

Multi-model audit (17 findings fixed)

Full codebase audit by Gemini 2.5 Pro, Claude Opus 4.6, and Kimi k2.5:

  • Critical: MPMC AcqRel ordering, Pod derive #[repr(C)] check, enum transmute docs
  • High: Weak backpressure trackers, Pipeline Drop/panic propagation, derive signed-int/usize fixes
  • Medium: prefault() assertion, MonitorWait docs, NUMA bounds check, Pipeline Lagged spin

Performance

  • PREFETCHW on x86 publisher (-10.7% fanout 1 sub, -14.6% fanout 2 subs)
  • WFE in recv() Phase 2 on AArch64 (~12ns cache-line-event wakeup)
  • Dead rdtsc asm block removed (~20-25 cycles saved per UMWAIT/TPAUSE)

Benchmarks (default vs atomic-slots, x86-64)

Benchmark Default atomic-slots
Publish only 1.94 ns 1.98 ns
Fanout 10 subs 15.68 ns 15.96 ns
Cross-thread 96.2 ns 95.2 ns
MPMC 1p 1s 12.23 ns 11.99 ns

CI improvements

  • MSRV 1.94 verification job
  • photon-ring-derive auto-publish
  • atomic-slots feature gate job
  • Test timeouts to prevent CI hangs

Research

3 research documents exploring seqlock alternatives via constraint-anchored analysis. All 3 independent agents converged on the same design. See docs/research-*.md.

Full Changelog: v2.3.0...v2.4.0

v2.3.0 — Message derive macro

17 Mar 23:25

Choose a tag to compare

Zero-friction Pod conversion with #[derive(Message)]:

#[repr(u8)]
#[derive(Clone, Copy)]
enum Side { Buy = 0, Sell = 1 }

#[derive(photon_ring::DeriveMessage)]
struct Order {
    price: f64,
    qty: u32,
    side: Side,           // enum → u8 automatically
    filled: bool,         // bool → u8 automatically
    tag: Option<u32>,     // Option → u64 automatically
}

// Generated: OrderWire (Pod) + From<Order> + From<OrderWire>
let wire: OrderWire = order.into();
publisher.publish(wire);
let order: Order = subscriber.recv().into();

Also: channel.rs and topology.rs split into module directories (no API changes).

v2.2.0 — DependencyBarrier

17 Mar 13:25

Choose a tag to compare

Consumer dependency graphs for ordered multi-stage processing.

let mut upstream_a = subs.subscribe_tracked();
let mut upstream_b = subs.subscribe_tracked();
let barrier = DependencyBarrier::from_subscribers(&[&upstream_a, &upstream_b]);
let mut downstream = subs.subscribe_tracked();

// downstream won't read event N until both A and B have finished it
let value = downstream.recv_gated(&barrier);

Zero cost on the default path — independent subscribers pay nothing.

10 new tests. 132 total.

v2.1.1

17 Mar 13:05

Choose a tag to compare

  • Remove unnecessary unsafe from cpuid (safe in Rust 1.94+)
  • MSRV bumped to 1.94
  • CI auto-publishes to crates.io on tagged releases
  • Fixed clippy lints

v2.1.0 — MPMC Deadlock Fix, UMWAIT Strategies, Prefetch

17 Mar 12:18

Choose a tag to compare

Highlights

Critical Bug Fix

  • MPMC catch_up_cursor deadlock — After ring wraparound, the shared cursor could strand permanently, causing all subscribers to spin forever. One-character fix: !=< in the successor stamp check. Any MPMC channel publishing more messages than its capacity was affected. (#1)

New Wait Strategies

  • MonitorWait — UMONITOR/UMWAIT on Intel Alder Lake+ with runtime CPUID WAITPKG detection. Near-zero power, ~30 ns wakeup latency. Safe constructor: WaitStrategy::monitor_wait(&AtomicU64). Falls back to PAUSE on older x86, SEVL+WFE on aarch64. (#2)
  • MonitorWaitFallback — TPAUSE timed C0.1 pause without address monitoring. Same platform fallbacks.

Performance

  • Prefetch next slot on publishPREFETCHT0 (x86) / PRFM PSTL1KEEP (ARM) hides the RFO stall. Applied to all SPMC and MPMC publish paths. (~15-25% write latency reduction) (#3)
  • recv_with() direct slot access — Pre-computes slot pointer outside the spin loop, eliminating per-iteration try_recv() overhead.
  • Cached has_backpressure on Publisher — Avoids Arc deref on every lossy publish.
  • WFE in MPMC predecessor spin — Low-power wait on aarch64 instead of YIELD.

Correctness

  • write_volatile/read_volatile in seqlock — Eliminates formal UB when a reader observes a partially-written slot. Zero runtime cost. (#4)
  • Removed dead count field from SubscriberGroup — Was always equal to const generic N.

Review

Three rounds of automated review with OpenAI Codex CLI. All findings addressed.

Upgrade

[dependencies]
photon-ring = "2.1.0"

No breaking API changes. WaitStrategy has two new variants (MonitorWait, MonitorWaitFallback); existing match arms are unaffected if using _ wildcard.

Full Changelog: v2.0.0...v2.1.0

v2.0.0 — Pod Trait, Derive Macro, Safety Contract

17 Mar 07:45

Choose a tag to compare

Photon Ring v2.0.0

BREAKING CHANGE: T: Copy replaced with unsafe trait Pod across the entire API.

Why this matters

The seqlock read protocol performs optimistic non-atomic reads that may produce torn bit patterns. Previously, the API accepted any T: Copy, including types like bool, char, and NonZero<u32> where certain bit patterns are invalid — making torn reads technically undefined behavior.

Pod ("Plain Old Data") guarantees that every bit pattern is valid, eliminating this class of UB entirely. The trait is pre-implemented for all numeric primitives, arrays, and tuples up to 12 elements.

Migration

// Before (v1.x)
let (mut p, s) = channel::<u64>(1024);

// After (v2.0) — primitives just work (Pod is pre-implemented)
let (mut p, s) = channel::<u64>(1024);

// Custom structs need unsafe impl:
#[repr(C)]
#[derive(Clone, Copy)]
struct Quote { price: f64, volume: u32 }
unsafe impl photon_ring::Pod for Quote {}

// Or with the derive macro (optional feature):
// photon-ring = { version = "2", features = ["derive"] }
#[derive(Clone, Copy, photon_ring::DerivePod)]
#[repr(C)]
struct Quote { price: f64, volume: u32 }

Other changes

  • try_publisher() on Photon<T> and TypedBus (returns Option, no panic)
  • Verification README: explicit limitations (SPMC-only, SC-only model)
  • Benchmark methodology documentation
  • 118 tests, all passing

v1.0.1

17 Mar 07:25

Choose a tag to compare

Updated benchmarks with M1 Pro numbers, banner, throughput ranges. No code changes from v1.0.0.

v1.0.0 — Topology Builder, O(1) Fanout, Payload Scaling

16 Mar 19:10

Choose a tag to compare

Photon Ring v1.0.0

The first major release. Addresses every LMAX Disruptor feature gap identified in independent review.

Highlights

Topology Builder

let (mut pub_, stages) = Pipeline::builder().capacity(1024).input::<RawTick>();
let (mut output, pipeline) = stages
    .then(|tick| enrich(tick))
    .then(|tick| generate_signal(tick))
    .build();

Dedicated thread per stage, graceful shutdown, fan-out support.

O(1) Subscriber Group Fanout

2.8 ns for 1, 10, or 1000 subscribers. Single cursor, compiler-unrolled.

Batch Processing

  • recv_batch(&mut self, buf) — fill a slice in one call
  • drain() — iterator over all available messages
  • Shutdown — coordinated graceful termination

Payload Scaling

Benchmarked 8B to 4KB payloads. Photon Ring outperforms Disruptor at every size.

Payload Scaling

Performance

Metric Value
One-way latency (RDTSC p50) 48 ns
Cross-thread roundtrip 96 ns
Publish cost 2.9 ns
SubscriberGroup (any N) 2.8 ns

Quality

  • 117 tests (28 unit + 69 integration + 20 doc)
  • MIRI verified (single-threaded)
  • TLA+ formal specification
  • 944-line academic technical report
  • Codex CLI reviewed (2 rounds, all findings addressed)
  • GitHub Actions CI (9 jobs, cross-platform)

Full changelog: https://github.com/userFRM/photon-ring/blob/master/CHANGELOG.md