# Plans: windows-ioring-sys
Completed checklists are recorded in [COMPLETED-PLANS.md](COMPLETED-PLANS.md) once there are any, and the milestones they
contained are archived in [COMPLETED-CHECKLIST.md](COMPLETED-CHECKLIST.md). Design decisions are in
[DESIGN-NOTES.md](DESIGN-NOTES.md).
| Path to CHECKLIST.md | Status | Brief description | Design Notes |
|---|---|---|---|
| [CHECKLIST.md](CHECKLIST.md) | in progress | Memory-safe Rust over the Windows 11 / Server 2022 `IoRing` submission/completion ring, as a separate crate from `windows-overlapped-io-sys` (duplicate-then-decide). Covers ring lifecycle and capability negotiation, zero-allocation token-owned buffers, the batch submission builder, threadless delivery through `ThreadpoolWait`, file/buffer registration, consumer-facing documentation, and the `ring-copy` topology-aligned sample (M1-M7 archived). The pinned-thread (Model B) architecture remains parked as `M6+` by the engineer's explicit direction. `M8` (complete) closed a PR #20 review finding: `FileRef::Raw(HANDLE)`'s lifetime gap, fixed with `unsafe fn` raw entry points plus a safe, `Arc<OwnedHandle>`-backed `SharedFile` wrapper for the common case. `M9` (complete) closed further PR #20 review findings: cross-ring `Token`/`RegisteredFile`/`RegisteredBuffers` confusion (a new per-ring `RingId`, checked at claim/push time), `PendingBufferRegistration` freeing its buffers instead of leaking them on an unclaimed drop, and `Batch::do_submit` letting `Drop` silently retry an already-attempted, already-failed submit. `M10` (active) finished auditing the ring completion contract against all ten specification-gap categories (M10.1-M10.3 complete): category 3 found that `supports` answers for the kernel's op table rather than this crate's push surface and that the registration one-shot is spent by queueing rather than succeeding (D-28); categories 1, 2, 6, 8 and 9 established the load-bearing rule that **every successfully queued SQE produces exactly one completion**, plus that this crate deliberately joins nothing (D-29, D-30); and D-14's registration-index continuity assumption was **dissolved rather than measured** -- the collision it guarded against needs a second registration, which was forbidden the day after D-14 was written, leaving only the reserved-not-confirmed meaning of the public counts to state (D-31). The audit also surfaced two API gaps, now queued as work rather than left in the design notes: `M10.4` (complete) gave `FileRef::Registered` safe entry points, since a registered index carries no lifetime obligation and the `unsafe` guarding it was vacuous (D-29): the safe pushes are now generic over a sealed `FileTarget` trait whose associated `Guard` type carries the one real difference between the two targets, which also made the fully-registered (registered file *and* registered buffer) combination expressible for the first time, non-breakingly (D-33). Investigating M10's own recorded test failures then found a **live use-after-free in shipped 0.1.2** and fixed it as `M10.6`: `BuildIoRingRegisterBuffers` reads its `IORING_BUFFER_INFO` array when the op runs rather than at build time -- the opposite of its file-handle sibling, which the rustdoc had wrongly generalized across -- so the array is now owned by the `IoRing` (D-32). `M10.5` (complete) added named predicates for the conditions a consumer must branch on -- `IORING_E_SUBMISSION_QUEUE_FULL` above all, which every push's rustdoc names as the backpressure signal but which `io::Error::kind()` cannot discriminate (D-30): a complete `RingCondition` enum, predicates for the runtime-actionable conditions, and a sealed `IoRingErrorExt` that puts them on `io::Error` so the downcast is named once rather than hand-rolled per call site (D-34). **M10 is complete.** **`M11` is complete and archived** (2026-08-28): it made the completion event a ring primitive, prompted by an external consumer proposal. `IoRing::completion_event` returns an owned duplicate of the ring's own event so a caller can wait on the ring alongside other handles without surrendering it (D-20); its contract is pinned by eleven sabotage-verified tests; `EventDelivery` is re-expressed on top of it, leaving one `SetIoRingCompletionEvent` call site; `windows-threadpool-sys` moved behind a default-on `threadpool` feature with CI building both combinations (D-22); the wakeup shapes and the barrier's ring-edge limit were swept across every place that states them; and `examples/model_b_multiplexed.rs` works the multiplexed shape end to end. The spike that answered the proposal established that the completion event is **edge-triggered** on the completion queue going empty to non-empty (D-19) -- which also exposed a live bug in shipped 0.1.2, where `EventDelivery` permanently stranded completions queued before handover, fixed in M11.3 by the same change that consolidated `EventDelivery` onto the new primitive. **`M12` is complete and archived** (2026-08-28): it addressed durability, from the same exchange. A spike had established that a flush **without** `DRAIN_PRECEDING_OPS` does not cover preceding writes (D-23) while the barrier that fixes it is a full ring-wide stall spanning submissions (D-24), making `Batch::flush` with default options a silent data-loss bug rather than a missing feature. `Batch::flush`/`flush_raw` now require an explicit `FlushCoverage`, so that spelling no longer exists; a `NO_BUFFERING` integration test proves the barrier's behaviour rather than its flag, and measured that *which direction* the reordering shows in is device-dependent (amending D-23); and the parameters the crate had hardcoded away are exposed as `WriteCaching` (`FILE_WRITE_FLAGS`) and `FlushMode` (`FILE_FLUSH_MODE`, whose `NoSync` is the one mode that commits nothing). Durability had been absent from `lib.rs` and `README.md` entirely, and both now state the three facts. **`M13` is complete and archived** (2026-08-29): the `epoch_log` sample is the vehicle D-26 makes for carrying durability *policy* to consumers without this crate owning it. Its own durability contract is written down first, in its own words (Design Autonomy), then implemented -- records composed into a registered arena and appended, epochs closed by one covering flush whose completion is what makes `is_durable` answer `true`, and a multiplexed wait on the ring's completion event alongside a shutdown latch. The replay pass is what turns it from a demonstration into evidence: it holds the durable region to a strict standard, tolerates a torn tail as the contract requires, and is itself proved able to fail by a negative control. Writing it also found and fixed a gap in the crate (D-35: per-buffer outstanding accounting and `RegisteredBuffers::get_mut`). **`M14` is complete and archived** (2026-08-29): the second half of the sample, covering the two things the ring cannot do for a consumer. A non-ring `FSCTL` (`FSCTL_SET_ZERO_DATA`, reclaiming a retired segment) is ordered against ring epochs by the log itself, since `drain_preceding` orders SQEs against SQEs (D-24) and reaches across neither the ring boundary nor a second ring; a thread-pool control plane runs checkpointing as Model A on a *second* ring while the log thread keeps Model B for the data path, because D-21 forbids a ring given to `EventDelivery` also being waited on directly -- so the ordering chain crosses log thread to pool thread to reclaim worker and back with the log thread blocking for none of it. All three epoch-commit strategies are implemented behind one interface, checked both by replay and by requiring the three to be **byte-identical**, and measured on the running machine. The measurement's finding is that the three are **indistinguishable** here -- the cross-strategy spread is the size of one strategy's run-to-run spread, because every strategy pays one device flush per epoch at hundreds of microseconds while their real differences land in the tens -- and it found two harness bugs nothing else caught, including one where a barrier benchmark that awaits each commit before appending again measures the barrier as free. Both findings are promoted into "Durability on the ring" in [DESIGN-NOTES.md](DESIGN-NOTES.md), since both are about the design rather than the demonstration. **`M15`-`M18` (not started) are the testing-strategy response to the eight defects the 0.1.x line and the M11-M14 branch produced.** They are organised by *defect population* rather than by technique, because the populations need different tools and one of them needs a tool that does not exist: (A) preconditions never varied -- every `event_delivery` test handed over a fresh ring, which is why [#47](https://github.com/MikeGrier/windows-threadpool-sys/issues/47) survived; (B) failure paths never taken -- no test ever ran `completion.result()` returning `Err`, which is why the checkpoint path could authorise a reclaim after a failed write; and (C) *permissions rather than behaviour* -- `&mut Vec<u8>` permits `reserve`/`resize`/reassign though no code path performs it, which is [D-35](DESIGN-NOTES.md#d-35) and [D-36](DESIGN-NOTES.md#d-36), the two most severe findings, and **no runtime technique reaches that population at all**. M15 and M16 gate the 0.2.0 release. M15 is deterministic memory instrumentation: a guard-page global allocator, chosen over Application Verifier / PageHeap by measurement rather than assumption ([D-37](DESIGN-NOTES.md#d-37) -- PageHeap works and `reg add` alone is enough, but IFEO is keyed by image file name and one test target produced six distinct hashed names in a day), plus a tracked poison pattern covering the gap guard pages structurally cannot see ([D-38](DESIGN-NOTES.md#d-38) -- a guard page catches access to memory that should not be touched, and is blind to the kernel writing into a live, valid buffer, which is exactly what `write_registered` and `read_registered` promise it will not do). M16 makes the contract executable: a public `RingContract` rendering [the category-2 rule](DESIGN-NOTES.md#one-sqe-one-completion), "one SQE, exactly one completion", checkable rather than merely stated, plus a fault-injection seam that finally takes the failure paths. M17 covers A by generating over the operation space instead of enumerating it by hand, gated on an explicit decision about randomized sampling that this component's conventions require be approved and recorded rather than assumed. M18 covered C, where review is a *primary* technique rather than a backstop, and added `cargo-mutants` against a measured rate of vacuous tests. **M8 through M18 are now complete and archived**, leaving only `M6+`, which is parked rather than pending. The strategy as a whole -- which technique reaches which population, what each one actually found, and what none of them reach -- is recorded in [DESIGN-NOTES.md](DESIGN-NOTES.md#testing-strategy-m185); mutation coverage went from 79.7% to 95.8%, and the borrow-surface audit found one further defect of the same shape as the two that prompted it ([D-43](DESIGN-NOTES.md#d-43)). A mock `IoRing` was considered and **rejected**: both shipped defects were the kernel behaving differently from this crate's assumptions, so a mock would have encoded the same assumptions and passed both bugs green. | [DESIGN-NOTES.md](DESIGN-NOTES.md), [DESIGN-SESSION-2026-08-28-completion-event-multiplexing.md](design-sessions/DESIGN-SESSION-2026-08-28-completion-event-multiplexing.md), [DESIGN-SESSION-2026-08-28-external-consumer-correspondence.md](design-sessions/DESIGN-SESSION-2026-08-28-external-consumer-correspondence.md) |