You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: atomic-slots — formally sound seqlock via AtomicU64 stripes (#14)
Adds `atomic-slots` feature flag: formally sound slot implementation using
AtomicU64 stripes. Zero perf cost on x86-64 (identical MOV instructions).
~5-10ns ARM64 reader overhead. Miri-passable. 8 new tests. All docs updated.
Discovered via 3-agent constraint-anchored analysis — all 3 converged
independently on the same design.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
**Ultra-low-latency SPMC/MPMC pub/sub using seqlock-stamped ring buffers.**
13
+
**Ultra-low-latency SPMC/MPMC pub/sub using stamped ring buffers.**
14
14
15
-
Photon Ring is a zero-allocation pub/sub crate for Rust built around pre-allocated ring buffers, per-slot seqlock stamps, and `T: Pod` payloads. It targets the part of concurrent systems where queueing overhead dominates: market data, telemetry fanout, staged pipelines, and other hot-path broadcast workloads where every subscriber should see every message.
15
+
Photon Ring is a zero-allocation pub/sub crate for Rust built around pre-allocated ring buffers, per-slot stamp validation, and `T: Pod` payloads. It targets the part of concurrent systems where queueing overhead dominates: market data, telemetry fanout, staged pipelines, and other hot-path broadcast workloads where every subscriber should see every message.
16
+
17
+
By default, slots use a volatile-based seqlock for maximum performance. With the `atomic-slots` feature, the same stamp protocol operates over `AtomicU64` stripes — **formally sound under the Rust abstract machine** with zero performance regression on x86-64.
16
18
17
19
It is `no_std` compatible with `alloc`, supports named-topic buses and typed buses, and includes a pipeline builder for multi-stage thread topologies on supported desktop/server platforms.
18
20
@@ -55,6 +57,7 @@ Optional features:
55
57
56
58
-`derive`: enables `#[derive(photon_ring::DerivePod)]` for user-defined `Pod` types.
57
59
-`hugepages`: enables Linux memory controls such as `mlock`, `prefault`, and NUMA helpers.
60
+
-`atomic-slots`: enables formally sound slot implementation using `AtomicU64` stripes instead of `write_volatile`/`read_volatile`. Zero performance cost on x86-64; ~5-10ns reader overhead on ARM64 due to acquire fence. Eliminates formal undefined behavior under the Rust abstract machine. Passes Miri.
58
61
59
62
Rust 1.94+ is supported. For best performance, compile with `-C target-cpu=native` to enable `PREFETCHW` and other CPU-specific optimizations.
> Photon Ring uses a seqlock-style optimistic read: a subscriber may speculatively copy a slot while a writer is updating it, then reject the value if the stamp changed. This pattern is sound for `T: Pod`, but not for arbitrary `Copy` types. If a torn bit pattern could be invalid for `T`, the read would be undefined behavior before Photon Ring had a chance to discard it.
210
+
### The `Pod` trait
211
+
212
+
The `Pod` trait means more than `Copy`: every possible bit pattern of the payload must be valid. This is required because the stamp-based read protocol may speculatively read bytes from a slot while a writer is updating it. If a torn bit pattern could be invalid for `T`, the read would be undefined behavior before the stamp check could discard it.
209
213
210
-
The `Pod` trait means more than `Copy`: every possible bit pattern of the payload must be valid. Primitive numerics, arrays of `Pod`, and tuples of `Pod` are already supported. For your own structs, use `#[repr(C)]`, stick to `Pod` fields, and implement `Pod` manually or via the `derive` feature when appropriate.
214
+
Primitive numerics, arrays of `Pod`, and tuples of `Pod` are already supported. For your own structs, use `#[repr(C)]`, stick to `Pod` fields, and implement `Pod` manually or via the `derive` feature when appropriate.
211
215
212
216
| Type | Why it is not `Pod`| Use instead |
213
217
|---|---|---|
@@ -219,6 +223,22 @@ The `Pod` trait means more than `Copy`: every possible bit pattern of the payloa
219
223
|`&T`, `&str`| Pointers must be valid | Value types only |
220
224
|`String`, `Vec<_>`| Heap-owning, has `Drop`| Fixed `[u8; N]` buffer |
221
225
226
+
### Formal soundness
227
+
228
+
Photon Ring offers two slot implementations, selectable at compile time:
|**Precedent**| Same pattern as Linux kernel seqlocks (20+ years) | Novel: first formally-sound seqlock in Rust |
238
+
239
+
> [!NOTE]
240
+
> The default volatile-based implementation is **correct on all real hardware** (x86, ARM). The "UB" is purely under Rust's abstract machine — no compiler has ever miscompiled this pattern, and the Linux kernel relies on identical semantics. Enable `atomic-slots` if you need formal soundness, Miri compliance, or defense against hypothetical future compiler optimizations.
241
+
222
242
> [!TIP]
223
243
> Keep rich domain types at the edges and publish compact `Pod` messages in the middle. Convert enums, `Option`, booleans, and strings into explicit numeric fields or fixed-size buffers before calling `publish`.
Copy file name to clipboardExpand all lines: docs/discussion-seqlock-soundness-and-no_std.md
+14Lines changed: 14 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -155,3 +155,17 @@ The multi-model audit and fixes addressed code-level correctness. What remains f
155
155
The seqlock's formal UB under Rust's abstract machine is not fixable today. It is not a photon-ring problem — it is a Rust language problem. Every seqlock, Disruptor, and shared-memory IPC implementation in Rust has the same gap. The hardware semantics are well-defined. The language spec will catch up (`atomic_memcpy`). Until then, photon-ring documents the gap honestly and relies on the same hardware guarantees that the Linux kernel has relied on for two decades.
156
156
157
157
The `no_std` constraint is defensible for the core ring layer but should not constrain cold-path decisions. A hybrid `std`-default approach would give most users better primitives while preserving bare-metal compatibility for the niche that needs it.
158
+
159
+
---
160
+
161
+
## 7. Update: `atomic-slots` Feature Implemented
162
+
163
+
The ASCL design proposed by the constraint-anchored analysis has been implemented
164
+
as the `atomic-slots` feature flag. Benchmarks confirm the research prediction:
165
+
zero performance regression on x86-64 (identical MOV instructions). On ARM64,
166
+
one extra DMB barrier in the reader (~5-10ns).
167
+
168
+
Users who need formal soundness can opt in via:
169
+
```toml
170
+
photon-ring = { version = "2.3.0", features = ["atomic-slots"] }
Copy file name to clipboardExpand all lines: docs/hardware-seqlock-alternatives.md
+7Lines changed: 7 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -591,3 +591,10 @@ C++ has the same problem. `std::atomic` maxes out at the largest lock-free atomi
591
591
The Linux kernel "solves" this by defining its own memory model (LKMM) that IS aware of hardware semantics and explicitly allows torn reads of non-atomic memory when gated by a sequence counter. Rust has no equivalent escape hatch.
592
592
593
593
The `atomic_memcpy` RFC (rust-lang/rfcs#3301) would add `atomic_load_bytes`/`atomic_store_bytes` that perform element-wise atomic operations on a byte range. This would make the existing seqlock pattern formally sound without any code changes. Until it lands, Mechanism #12 (explicit decomposition into atomic fields) is the correct engineering solution.
594
+
595
+
---
596
+
597
+
## Implementation Status
598
+
599
+
Mechanism #12 (Atomic Seqlock) has been implemented as the `atomic-slots` feature.
600
+
Confirmed: zero performance regression on x86-64. See CHANGELOG.md.
Copy file name to clipboardExpand all lines: docs/research-seqlock-alternatives.md
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -608,3 +608,11 @@ The actual issue is that `UnsafeCell<MaybeUninit<T>>` accessed via raw pointer d
608
608
The prohibition analysis identified one viable, novel design that achieves formal soundness under Rust's abstract machine with zero performance regression on x86-64: **Atomic Stripe Compile-Time Layout (ASCL / Design 6)**. The design decomposes `T: Pod` into `[AtomicU64; N]` at compile time and uses the existing seqlock stamp protocol with `Relaxed` atomic stores/loads for the payload fields. On x86-64, this produces identical machine code to the current volatile-based implementation. On ARM64, it adds one `DMB ISHLD` barrier in the reader path (~5-10 ns).
609
609
610
610
The impossibility proofs demonstrate that no other approach can achieve formal soundness at this performance level for payloads > 16 bytes. All spatially-separated designs (ping-pong, epoch-indexed, write-once) reduce to the seqlock race when slots are recycled. All coordination-based designs exceed the latency budget. The only viable path is decomposition into existing atomic primitives.
611
+
612
+
---
613
+
614
+
## Implementation Status
615
+
616
+
Design 6 (ASCL / Atomic Stripe Compile-Time Layout) has been implemented as the
617
+
`atomic-slots` feature in photon-ring v2.3.0. Benchmark results confirm the
618
+
zero-cost prediction on x86-64. See CHANGELOG.md for details.
0 commit comments