-
Notifications
You must be signed in to change notification settings - Fork 0
feat(event): ADR-0010 — bounded page rotation with generation-tagged reuse (design + prototype) #1869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(event): ADR-0010 — bounded page rotation with generation-tagged reuse (design + prototype) #1869
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,288 @@ | ||||||||||||||||||||
| # ADR-0010: Bounded page rotation with generation-tagged reuse | ||||||||||||||||||||
|
|
||||||||||||||||||||
| **Status:** Proposed | ||||||||||||||||||||
| **Date:** 2026-07-14 | ||||||||||||||||||||
| **Issue/PR:** pending | ||||||||||||||||||||
|
|
||||||||||||||||||||
| > **Design-first ADR.** No implementation accompanies this document. Per | ||||||||||||||||||||
| > [architecture.md](../architecture.md) §14 and the ADR index rule, a Proposed | ||||||||||||||||||||
| > ADR permits only unwired prototypes, and this one intentionally lands before | ||||||||||||||||||||
| > any prototype so the rotation state machine is reviewed as a design, not as | ||||||||||||||||||||
| > a diff. ADR-0009 §"Revisit when" required exactly this stop. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Context | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ADR-0009's `VolatileAdmissionChannel<N>` binds one preallocated | ||||||||||||||||||||
| published-prefix page (ADR-0008) to one SPSC descriptor ring (ADR-0006). When | ||||||||||||||||||||
| the page's byte or descriptor capacity is exhausted the lane is finished: the | ||||||||||||||||||||
| typed `page full` error is terminal for the channel's useful life, and the | ||||||||||||||||||||
| producer's only recovery is to tear down and rebuild the composite, losing the | ||||||||||||||||||||
| ring, sequence continuity, and the consumer binding. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| A production event fabric cannot run on one page per lane lifetime. It needs | ||||||||||||||||||||
| the producer to continue admitting into a fresh page while the consumer | ||||||||||||||||||||
| finishes draining the previous one — with the number of simultaneously live | ||||||||||||||||||||
| pages **bounded by construction**, page memory **reused** rather than | ||||||||||||||||||||
| reallocated, and reuse **provably safe** against stale descriptors addressing | ||||||||||||||||||||
| a recycled page (the ABA problem). | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Scope boundaries inherited from ADR-0009 remain: one producer, one consumer, | ||||||||||||||||||||
| volatile, process-local, unwired, non-authoritative. This ADR adds only | ||||||||||||||||||||
| rotation, bounded outstanding pages, and generation-tagged reuse. It does | ||||||||||||||||||||
| **not** add WAL durability/replay, authenticated registry lookup, NUMA pool | ||||||||||||||||||||
| placement policy, priority lanes, crossbeam-epoch, multi-producer or | ||||||||||||||||||||
| multi-consumer topology, or any protected-evidence semantics. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Decision | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Add a `RotatingAdmissionChannel<N, P>` in `aegis-event`: one SPSC descriptor | ||||||||||||||||||||
| ring bound to a **fixed pool of `P >= 2` identically-configured page slots**, | ||||||||||||||||||||
| all preallocated at construction. No allocation, deallocation, or `Arc` | ||||||||||||||||||||
| reference-count traffic occurs after `split`. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Page epochs and slot addressing | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Pages are identified by a monotonically increasing **page epoch** `e` | ||||||||||||||||||||
| (`u64`, starting at 0). Epoch `e` occupies pool slot `e mod P` and stamps its | ||||||||||||||||||||
| descriptors with `arena_generation = e as u32` (wrapping). The consumer | ||||||||||||||||||||
| recovers the slot index from a descriptor as | ||||||||||||||||||||
| `descriptor.arena_generation as u64 mod P` and then requires **exact | ||||||||||||||||||||
| generation equality** with the slot's currently-bound page before any | ||||||||||||||||||||
| descriptor-cell or payload access — the same identity check ADR-0007/0008 | ||||||||||||||||||||
| already enforce, now doing double duty as the reuse guard. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Sequence space is continuous across pages: page epoch `e+1` is constructed | ||||||||||||||||||||
| with `first_sequence` equal to the sealed page `e`'s `next_sequence`, so ring | ||||||||||||||||||||
| sequence and page sequence remain a single unbroken wrapping sequence exactly | ||||||||||||||||||||
| as in ADR-0009. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Producer rotation algorithm | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Rotation is attempted only inside admission, only on the typed | ||||||||||||||||||||
| `page full` / `descriptor full` validation results, and **before** ring | ||||||||||||||||||||
| reservation — a rotated-then-admitted event keeps ADR-0009's phase discipline | ||||||||||||||||||||
| unchanged: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```text | ||||||||||||||||||||
| validate payload length against the ACTIVE page | ||||||||||||||||||||
| on page/descriptor full: | ||||||||||||||||||||
| Acquire-load consumer released_epoch [A] | ||||||||||||||||||||
| require active_epoch + 1 <= released_epoch + P (slot free?) | ||||||||||||||||||||
| on failure: return typed PageQuotaExhausted — nothing mutated | ||||||||||||||||||||
|
Comment on lines
+69
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is an initialization/sentinel bug in the quota check protocol.\n\nIf
Suggested change
Comment on lines
+68
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift Data race / ABA guard flaw: The rotation algorithm uses an ambiguous
📍 Affects 2 files
🤖 Prompt for AI Agents |
||||||||||||||||||||
| Release-close the active page writer (seal, ADR-0008) [S] | ||||||||||||||||||||
| rebind slot (active_epoch + 1) mod P: | ||||||||||||||||||||
| new generation = (active_epoch + 1) as u32 | ||||||||||||||||||||
| first_sequence = sealed page next_sequence | ||||||||||||||||||||
| active_epoch += 1 | ||||||||||||||||||||
| re-validate payload against the fresh page | ||||||||||||||||||||
| then the unchanged ADR-0009 admission sequence: | ||||||||||||||||||||
| reserve ring slot, InFlight, append + page Release [P], | ||||||||||||||||||||
| ring slot write, ring Release [R], Open | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Properties: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - **Bounded work, no waiting.** Rotation is a seal, one Acquire load, one | ||||||||||||||||||||
| slot re-initialization over preallocated memory, and bookkeeping. If the | ||||||||||||||||||||
| successor slot is still outstanding, the producer returns | ||||||||||||||||||||
| `PageQuotaExhausted` with **zero page and zero ring mutation** — the caller | ||||||||||||||||||||
| applies its declared event-class policy (bounded retry, spool, or | ||||||||||||||||||||
| best-effort drop). No spin, sleep, allocation, or overflow queue. | ||||||||||||||||||||
| - **A payload larger than one empty page** is still the ADR-0008 typed | ||||||||||||||||||||
| invalid-input error; rotation never loops. | ||||||||||||||||||||
| - **Failure atomicity is preserved.** The quota check happens before the seal | ||||||||||||||||||||
| `[S]`, so a refused rotation leaves the active page open and usable for | ||||||||||||||||||||
| smaller payloads. An unwind during rotation follows ADR-0009's caught-unwind | ||||||||||||||||||||
| contract: ordered faulted closure of the page (sealed or fresh), terminal | ||||||||||||||||||||
| word, and ring. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Consumer release protocol | ||||||||||||||||||||
|
|
||||||||||||||||||||
| The ring is FIFO and consumption is strictly in-order (ADR-0009's exact | ||||||||||||||||||||
| expected-sequence rule), so descriptors arrive grouped by epoch in epoch | ||||||||||||||||||||
| order. The consumer tracks `current_epoch` and, upon validating the first | ||||||||||||||||||||
| descriptor of epoch `e+1`: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| 1. requires its per-epoch committed count to equal the sealed page `e`'s | ||||||||||||||||||||
| published count — a shortfall is the terminal orphaned-prefix/count | ||||||||||||||||||||
| mismatch of ADR-0009, now detected at the page boundary instead of only at | ||||||||||||||||||||
| end of stream; | ||||||||||||||||||||
| 2. Release-stores `released_epoch = e` `[E]`; | ||||||||||||||||||||
| 3. proceeds with ADR-0009 validation of the new epoch's descriptor against | ||||||||||||||||||||
| the freshly Acquire-loaded slot binding. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Clean or faulted end-of-stream releases the final epoch after the ADR-0009 | ||||||||||||||||||||
| terminal checks, which now aggregate: total committed count must equal the | ||||||||||||||||||||
| sum of sealed published counts plus the final page's published count. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Reclamation proof (why no epochs are needed) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| The single reclamation edge is consumer → producer over `released_epoch`: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```text | ||||||||||||||||||||
| last frame commit for epoch e, ring-tail Release [C] | ||||||||||||||||||||
| -> released_epoch Release-store [E] | ||||||||||||||||||||
| -> producer released_epoch Acquire [A] | ||||||||||||||||||||
| -> slot (e mod P) rebind and first cell write of epoch e+P | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - A payload lease (`AdmittedEvent`) mutably borrows the consumer, so no lease | ||||||||||||||||||||
| can be alive when the consumer later executes `[E]` inside `try_next` — the | ||||||||||||||||||||
| borrow checker, not a runtime count, proves no reader holds bytes of a page | ||||||||||||||||||||
| being released. This is the property crossbeam-epoch would otherwise buy, | ||||||||||||||||||||
| and it holds only because the topology is exactly one consumer; any | ||||||||||||||||||||
| multi-reader future (query snapshots, secondary indexes) must not reuse | ||||||||||||||||||||
| this argument and gets its own ADR. | ||||||||||||||||||||
| - The producer never rebinds a slot without Acquire-observing `[E]` for its | ||||||||||||||||||||
| previous occupant, so every cell write of epoch `e+P` happens-after every | ||||||||||||||||||||
| committed read of epoch `e`. | ||||||||||||||||||||
| - **ABA/wrap safety:** at most `P` epochs are ever live, and the ring is | ||||||||||||||||||||
| FIFO, so a descriptor observable by the consumer references an epoch in | ||||||||||||||||||||
| `[released_epoch, active_epoch]`, a window of width `<= P`. The `u32` | ||||||||||||||||||||
| generation tag is unambiguous while `P < 2^32`, which the type-level | ||||||||||||||||||||
| `P: usize` bound enforces absurdly early; the epoch counter itself is `u64` | ||||||||||||||||||||
| and non-wrapping for any realistic process lifetime (`2^64` rotations). | ||||||||||||||||||||
|
Comment on lines
+141
to
+144
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
On 64-bit platforms, 💡 Proposed fix- FIFO, so a descriptor observable by the consumer references an epoch in
- `[released_epoch, active_epoch]`, a window of width `<= P`. The `u32`
- generation tag is unambiguous while `P < 2^32`, which the type-level
- `P: usize` bound enforces absurdly early; the epoch counter itself is `u64`
+ FIFO, so a descriptor observable by the consumer references an epoch in
+ `[released_count, active_epoch]`, a window of width `<= P`. The `u32`
+ generation tag is unambiguous while `P < 2^32`, which a `const` assertion
+ must enforce (since `usize` allows larger values on 64-bit systems); the epoch counter itself is `u64`📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| A descriptor whose generation fails slot equality is the ADR-0009 terminal | ||||||||||||||||||||
| identity mismatch — never a skip, never a fallback read. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Terminal-state extension | ||||||||||||||||||||
|
|
||||||||||||||||||||
| The composite terminal word and its `OPEN/CLEAN/FAULTED` values are unchanged. | ||||||||||||||||||||
| `finish` seals the **active** page before storing `CLEAN` and closing the | ||||||||||||||||||||
| ring. Clean end-of-stream now additionally requires every sealed epoch to have | ||||||||||||||||||||
| been fully committed at its boundary (checked incrementally by the release | ||||||||||||||||||||
| protocol) — so the aggregate clean condition remains "total committed equals | ||||||||||||||||||||
| total published", with page-boundary early detection as a strengthening, not | ||||||||||||||||||||
| a replacement, of ADR-0009's end-of-stream checks. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Public result semantics | ||||||||||||||||||||
|
|
||||||||||||||||||||
| `AdmittedSequence` gains nothing: it already carries page identity, generation, | ||||||||||||||||||||
| and wrapping sequence. Success semantics are ADR-0009's verbatim — volatile | ||||||||||||||||||||
| publication only; never consumption, durability, a receipt, or authorization. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| New/changed typed errors: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| | Error | Page mutation | Ring mutation | Retry meaning | | ||||||||||||||||||||
| |---|---:|---:|---| | ||||||||||||||||||||
| | `PageQuotaExhausted` (all `P` slots outstanding) | none | none | consumer lagging; bounded retry/spool per event class | | ||||||||||||||||||||
| | `page full` / `descriptor full` | no longer surfaced when rotation succeeds; surfaced unchanged when the payload exceeds one empty page | none | correct input | | ||||||||||||||||||||
|
|
||||||||||||||||||||
| All other ADR-0009 error rows are unchanged. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Memory and ownership layout | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```text | ||||||||||||||||||||
| RotatingAdmissionChannel<N, P> | ||||||||||||||||||||
| pool: [page slot 0][page slot 1]...[page slot P-1] (preallocated) | ||||||||||||||||||||
| ring, terminal word (as ADR-0009) | ||||||||||||||||||||
| released_epoch: cache-line-aligned AtomicU64 (consumer writes, | ||||||||||||||||||||
| producer reads) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| producer: [active writer][active_epoch][pool handles][ring producer][terminal] | ||||||||||||||||||||
| consumer: [ring consumer][current_epoch][per-epoch committed][pool handles] | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| `released_epoch` lives on its own cache line: it is written once per page | ||||||||||||||||||||
| lifetime, not per event, so rotation metadata adds no steady-state false | ||||||||||||||||||||
| sharing to the `[P]`/`[R]`/`[C]` hot lines. Slot rebinding reuses the page | ||||||||||||||||||||
| allocation in place; the pool never grows, shrinks, or reallocates. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Failure, overload, and security behavior | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - `PageQuotaExhausted` is backpressure, not data loss: nothing is admitted, | ||||||||||||||||||||
| nothing is dropped, and the caller's event-class policy decides. The | ||||||||||||||||||||
| critical/WAL lane semantics remain future work — this channel still cannot | ||||||||||||||||||||
| carry protected evidence. | ||||||||||||||||||||
| - A slow or stalled consumer bounds producer memory at exactly `P` pages plus | ||||||||||||||||||||
| the ring; there is no unbounded queue anywhere in the composite | ||||||||||||||||||||
| (architecture law §2.7). | ||||||||||||||||||||
| - Generation mismatch, epoch-boundary count shortfall, sequence discontinuity, | ||||||||||||||||||||
| CRC failure, and corrupt terminal state all remain terminal, fail-closed | ||||||||||||||||||||
| lane errors — rotation adds detection points, never recovery-by-skipping. | ||||||||||||||||||||
| - Descriptors remain non-capabilities; slot addressing via | ||||||||||||||||||||
| `generation mod P` selects memory already owned by this channel's bound | ||||||||||||||||||||
| pool and never a registry, tenant, or foreign allocation. Authenticated | ||||||||||||||||||||
| registry lookup remains explicitly out of scope and future work. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Progress and performance hypothesis | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Steady-state admission and consumption are byte-for-byte the ADR-0009 paths; | ||||||||||||||||||||
| rotation adds one Acquire load on the page-full branch only. The hypothesis is | ||||||||||||||||||||
| that amortized cost per event is unchanged and rotation cost is `O(1)` | ||||||||||||||||||||
| bounded, paid once per page. No measurement accompanies this ADR; the fabric | ||||||||||||||||||||
| remains `target` with no performance claim, and qualification requirements are | ||||||||||||||||||||
| unchanged from ADR-0009. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Alternatives considered | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - **Allocate a fresh page per rotation, drop the old one** — rejected: | ||||||||||||||||||||
| per-page allocation/free in the hot path, unbounded live pages under a slow | ||||||||||||||||||||
| consumer, and no reuse story; violates the bounded-everything law. | ||||||||||||||||||||
| - **crossbeam-epoch reclamation now** — rejected: the SPSC borrow-checker | ||||||||||||||||||||
| argument above makes epochs redundant for this topology; epochs enter with | ||||||||||||||||||||
| multi-reader query snapshots (LLD §epoch retirement) under their own ADR. | ||||||||||||||||||||
| - **Per-slot busy/free atomic flags** — rejected: a single released-epoch | ||||||||||||||||||||
| counter is sufficient under FIFO in-order consumption and keeps one | ||||||||||||||||||||
| reclamation edge to prove instead of `P`. | ||||||||||||||||||||
| - **Producer blocks/spins when the pool is exhausted** — rejected: hot crates | ||||||||||||||||||||
| admit no unbounded waits; typed backpressure lets the declared event class | ||||||||||||||||||||
| decide. | ||||||||||||||||||||
| - **Encode slot index in `arena_id`** — rejected: `arena_id` identifies the | ||||||||||||||||||||
| lane/arena binding and participates in identity checks across the channel; | ||||||||||||||||||||
| overloading it conflates lane identity with position. The epoch already | ||||||||||||||||||||
| determines the slot. | ||||||||||||||||||||
| - **Skip the page-boundary count check and rely on end-of-stream totals** — | ||||||||||||||||||||
| rejected: boundary checking converts a silent mid-stream orphan into an | ||||||||||||||||||||
| immediate terminal error while the evidence is fresh. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Verification (required before the prototype can merge) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| The implementing PR must provide, mirroring ADR-0009's evidence classes: | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - rotation at byte exhaustion and at descriptor exhaustion; sequence | ||||||||||||||||||||
| continuity across the seam; admission token generations advancing; | ||||||||||||||||||||
| - `PageQuotaExhausted` with zero page/ring mutation, then successful admission | ||||||||||||||||||||
| after the consumer crosses the boundary; | ||||||||||||||||||||
| - generation-tagged reuse: slot rebinding after release, stale-descriptor | ||||||||||||||||||||
| injection failing identity terminally, wrap of the `u32` tag under a small | ||||||||||||||||||||
| `P` fixture; | ||||||||||||||||||||
| - epoch-boundary committed-count shortfall detected terminally at the seam; | ||||||||||||||||||||
| - caught-unwind fault injection at: before quota check, after seal `[S]` | ||||||||||||||||||||
| before rebind, after rebind before re-validation, plus all ADR-0009 points; | ||||||||||||||||||||
| - differential oracle (safe sealed pages + `VecDeque`) across many rotations | ||||||||||||||||||||
| with variable-length payloads; | ||||||||||||||||||||
| - Loom models for the release/rebind race (`[C]→[E]→[A]→rebind`), quota | ||||||||||||||||||||
| refusal versus in-flight release, and faulted closure mid-rotation; | ||||||||||||||||||||
| - full Miri; ASan/TSan lanes extended to the rotation suites; zero | ||||||||||||||||||||
| steady-state allocations including across a rotation. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Migration and rollback | ||||||||||||||||||||
|
|
||||||||||||||||||||
| While Proposed, nothing ships. The implementing PR lands | ||||||||||||||||||||
| `RotatingAdmissionChannel` as isolated, unwired prototype code beside the | ||||||||||||||||||||
| single-page composite, which remains the reviewed baseline and differential | ||||||||||||||||||||
| reference. Rollback deletes the rotating module and this ADR's index row; | ||||||||||||||||||||
| ADR-0006..0009 artifacts are untouched. Production wiring still additionally | ||||||||||||||||||||
| requires WAL durability/replay, authenticated registry lookup, NUMA-owner | ||||||||||||||||||||
| reclamation policy, priority lanes, shadow equality, qualification, and | ||||||||||||||||||||
| release-artifact rollback — rotation removes exactly one blocker from that | ||||||||||||||||||||
| list, not several. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Revisit when | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Revisit before adding WAL/durability classes, an authenticated page registry, | ||||||||||||||||||||
| NUMA placement or cross-node pools, priority lanes, crossbeam-epoch or any | ||||||||||||||||||||
| second reader, or any multi-producer/multi-consumer topology — each invalidates | ||||||||||||||||||||
| at least one proof above (most immediately the borrow-checker reclamation | ||||||||||||||||||||
| argument, which is single-consumer-only). | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## References | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - [Mandatory architecture law](../architecture.md) | ||||||||||||||||||||
| - [ADR-0007: sealed generation-tagged slab pages](0007-sealed-generation-tagged-slab-pages.md) | ||||||||||||||||||||
| - [ADR-0008: append-only published-prefix slab pages](0008-append-only-published-prefix-slab-pages.md) | ||||||||||||||||||||
| - [ADR-0009: failure-atomic single-page slab/ring admission](0009-failure-atomic-slab-ring-admission.md) | ||||||||||||||||||||
| - [Target LLD: epoch retirement and reclamation](../LLD.md) | ||||||||||||||||||||
| - [Migration matrix](../../MIGRATION_MATRIX.md#10-gap-analysis-against-target) | ||||||||||||||||||||
| - [Contribution and unsafe-code standard](../../CONTRIBUTING.md#lock-free-structures) | ||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a potential correctness issue in the slot addressing logic when the epoch$2^{32}$ if $2^{32}$ ).\n\nSince $(e \bmod 2^{32}) \pmod P$ . However, the producer assigns the slot using $2^{32}$ (for example, if $(e \bmod 2^{32}) \pmod P \neq e \pmod P$ for $e \ge 2^{32}$ .\nSpecifically, for $e = 2^{32}$ :\n- Producer slot: $2^{32} \bmod 3 = 1$ \n- Consumer slot: $(2^{32} \bmod 2^{32}) \bmod 3 = 0 \bmod 3 = 0$ \n\nThis discrepancy will cause the consumer to look in the wrong slot, leading to a generation mismatch and a terminal failure.\n\nRecommendation:\nExplicitly restrict $P = 2^k$ ). This ensures that $2^{32}$ (for any $P \le 2^{32}$ ), making the modulo mapping wrapping-safe, while also allowing the compiler to optimize the modulo operations to fast bitwise ANDs (
eexceedsPis not a power of two (or more generally, a divisor ofarena_generationis stored ase as u32(wrapping), the consumer recovers the slot index usingdescriptor.arena_generation as u64 mod P, which is equivalent toe mod P.\n\nIfPis not a divisor ofP = 3), thenPto be a power of two (e.g.,Palways dividese & (P - 1)).