windows-ioring-sys 0.9.0

Memory-safe Rust over the Windows IoRing submission/completion ring, with the ring holding each in-flight buffer until its completion.
Documentation
# Checklist: windows-ioring-sys

Design decisions are in [DESIGN-NOTES.md](DESIGN-NOTES.md); the session that produced them is
[DESIGN-SESSION-2026-08-22-ioring-architecture.md](design-sessions/DESIGN-SESSION-2026-08-22-ioring-architecture.md).
Everything through M18 is archived in [COMPLETED-CHECKLIST.md](COMPLETED-CHECKLIST.md): M1-M6
[here](COMPLETED-CHECKLIST.md#moved-2026-08-22----m1-through-m6-ring-lifecycle-through-consumer-documentation),
M7 [here](COMPLETED-CHECKLIST.md#moved-2026-08-23----m7-ring-copy-a-topology-aligned-sample), M11-M14 in their
own dated groups, M8-M10
[here](COMPLETED-CHECKLIST.md#moved-2026-08-30----m8-through-m10-handle-lifetime-cross-ring-identity-and-the-contract-audit),
and M15-M18
[here](COMPLETED-CHECKLIST.md#moved-2026-08-30----m15-through-m18-the-testing-strategy-response-to-eight-defects).

M19 is archived [here](COMPLETED-CHECKLIST.md#m19). `M20` through `M24`, and the `M21+`, `M22+` and
`M28+` buckets, are archived
[here](COMPLETED-CHECKLIST.md#moved-2026-10-01-m20-through-m24); `M26+` and `M28`
[here](COMPLETED-CHECKLIST.md#moved-2026-10-01-m26-and-m28); and `M27`
[here](COMPLETED-CHECKLIST.md#moved-2026-10-01-m27).

The plan these belonged to is recorded in [COMPLETED-PLANS.md](COMPLETED-PLANS.md); add a milestone
here and a row in [PLANS.md](PLANS.md) when new work is planned.

**`M26.13`, the pool stall, is not archived -- it was transferred.** The fault was never this
crate's, so the question, its timeline and its 33 measurement captures now live in
[windows-threadpool-sys](../windows-threadpool-sys/CHECKLIST.md) as `M-T7.1`. The reproducer stays
here, because the ring is what reaches the state.

**An `M{n}+` heading is parked rather than pending** -- the convention is noted here because the
archived groups use it: such a heading is gated work with no current obligation, not an unfinished
milestone. When this file carries milestones again, which of them are open is what the headings
say; this preamble deliberately does not restate it, because a second copy is one nobody updates.

## M-R1 -- The stall post-mortem after the hooking facility was removed

Opened 2026-10-03. `windows-threadpool-sys` deleted its inline hooking facility, because this
workspace ships no undocumented APIs and no assumptions about undocumented behaviour -- see
[DESIGN-NOTES.md](../../DESIGN-NOTES.md#no-undocumented-apis). The reproducer in this crate was its
real consumer, so what that costs the post-mortem is recorded here rather than left to be inferred
from an absence.

- [x] **M-R1.1** -- **Strip the post-mortem probes that depended on the hooking facility.** Done
      2026-10-03.

  **What was removed from `tests/event_delivery.rs`:**

  - the worker-factory counter and completion-port depth reads taken at the moment of failure, in
    `hand_rolled_trigger`, deliberately ordered *before* the liveness probe because submitting
    work is the one action measured to release this stall and asking afterwards would describe a
    pool the question had already repaired;
  - `poke_the_stalled_port`, gated on `IORING_POKE_PORT`, which posted a packet to a stalled port
    and read the factory's worker count either side. This is the experiment behind the recorded
    answer that **an ordinary completion-port arrival has never been seen to recover this stall**;
  - `capture_healthy_factory_state`, gated on `IORING_CAPTURE_HEALTHY`, the healthy-arm contrast
    for the same reading.

  **What the apparatus can no longer answer.** Whether a stalled factory has zero workers while
  holding queued work -- the signature the investigation turned on -- and whether poking the port
  changes that. The reproducer still detects the stall and still reports outstanding counts, ring
  state and pool liveness; what it has lost is the ability to say *why* the pool did not dispatch.

  **What survives, and where.** The captures taken with the facility are committed under
  `windows-threadpool-sys/measurements/`, and the central finding -- the pool's first worker is
  never created -- was reproduced independently of the hooks by an ETW kernel trace across 900
  runs (`M26.13.19`). ETW is the documented route and is where any rebuild should start.

- [ ] **M-R1.2** -- **Decide whether to rebuild the two experiments on ETW.** Not started, and
      deliberately not scoped yet.

  The two questions `M-R1.1` retired are worth answering and are not answerable in-process on
  documented ground. An ETW kernel trace reaches both: thread-create events say whether the
  stalled factory ever made a worker, and the same session spans the poke, so the before/after
  contrast survives the move. Whether it is worth building depends on whether `M-T7.1` is resumed,
  which is a decision for the engineer rather than something this item should assume.

## M29 -- The `epoch_log` sample treats a failed commit as repairable by a later one

Opened 2026-10-05 from the durable-ioring design session
([DESIGN-SESSION-2026-10-05-epoch-ring.md](../../design-sessions/DESIGN-SESSION-2026-10-05-epoch-ring.md),
Scenarios 1 and 2). Queued here rather than in [durable-ioring](../durable-ioring/CHECKLIST.md)
because the sample is wrong whatever becomes of that crate
([DI-D-7](../durable-ioring/DESIGN-NOTES.md#di-d-7)).

- [ ] **M29.1** -- **Stop letting a later successful commit report an earlier failed epoch durable.**
  `Committer::claim` in [commit.rs]examples/epoch_log/commit.rs argues under "A failed commit is
  not permanent, and that is not a loophole" that commit *N+1*'s covering flush settles a failed
  commit *N*, and advances `durable_through` past it. That holds only if the writes in the suspect
  window -- every write pushed before the failure was observed that no earlier successful covering
  flush covers -- are re-issued before
  the later flush. Without that, a lower layer that discarded the data on the failed flush (the
  documented Linux behaviour, and undocumented either way on Windows) leaves *N* reported durable
  and gone. Correct the code and its rustdoc so the watermark stays at the last epoch below the
  failure, and update `commit/tests.rs`, which presumably pins the current behaviour. **Sweep the
  same reasoning** in [checkpoint.rs]examples/epoch_log/checkpoint.rs (its `settle` advances by
  `max`; a later checkpoint rewrites the whole record, which is the re-issue case, so it may be
  correct -- argue it either way in the code) and in
  [strategy.rs]examples/epoch_log/strategy.rs's `settle`, and check
  [contract.rs]examples/epoch_log/contract.rs for a statement that relies on it.

## M32 -- Write-through needs an unbuffered handle

Opened 2026-10-07 from durable-ioring's `DI-3.2.2.1`, whose test of its caching option found this.

- [ ] **M32.1** -- **State, and pin, what `WriteCaching::WriteThrough` does on a cached handle.**
  Measured through dioring's ring on 2026-10-07: a write-through write to a handle opened for cached
  I/O -- including one opened with `FILE_FLAG_WRITE_THROUGH` -- completes with Win32 error 509 ("The
  specified operation is not supported on a file opened for cached IO"), and to a handle opened with
  `FILE_FLAG_NO_BUFFERING` as well, it completes as a transfer. This crate's documentation presents
  the flag as a cache directive a caller may add alongside a covering flush, and none of it says the
  handle must be unbuffered: `WriteCaching`'s rustdoc in `batch.rs`, the write push's in
  `batch/push.rs`, `lib.rs`, the README's durability section, and
  [D-25]DESIGN-NOTES.md#d-25. Sweep all of them. **Test in both directions:** the cached handle
  fails with 509, and the unbuffered one transfers, with a buffer and offset that meet unbuffered
  I/O's alignment rules (a page-aligned allocation, for instance), so the test does not pass by
  accident of the allocator. No crate test exercises write-through today, which is why this was not
  found here.

## M33 -- A wake-only delivery for a caller that owns its ring

Opened 2026-10-11 from durable-ioring's `DI-3.10.3.5.6`
([DI-D-53](../durable-ioring/DESIGN-NOTES.md#di-d-53)), whose backend owns its ring threadlessly and
needs the thread pool only to be told when to drain.

- [x] **M33.1** -- `CompletionWake`: the thread pool wakes an owner, who drains its own ring; `EventDelivery` re-expressed on it, so the delivery protocol is written once. -> [completed 2026-10-11]COMPLETED-CHECKLIST.md#m331

- [ ] **M33.2** -- **The engineer's review of the assistant's choices in `M33.1`**: the name
  `CompletionWake`; `new(&mut IoRing, on_wake, env)`; `on_wake: Fn() + Send + Sync + 'static`; one
  waiter per ring stated, not enforced, as D-21 is; `EventDelivery` holding a `CompletionWake`
  where it held a `ThreadpoolWait`, its explicit `Drop` folded into the wake's; the setup-signal
  sabotage moved to the wake as one site serving both; and the explicit drain in the wake's `Drop`
  recorded as an equivalent mutant, since `ThreadpoolWait`'s own `Drop` drains too and only the
  obligation report differs.

  > **-> CROSS-COMPONENT HANDOFF:** next work is in component `crates/durable-ioring` -> `DI-3.10.3.5` -> `DI-3.10.3.5.6.1` (pool delivery over the threadless backend, built on this wake). See [CHECKLIST.md]../durable-ioring/CHECKLIST.md.