Skip to main content

windows_ioring_sys/ring/
bookkeeping.rs

1// Copyright (c) 2026 Mike Grier
2// Split from ring.rs at 265a7a04.
3//! What the ring knows about itself: negotiated capabilities, the completion
4//! event, registration counts, the operation ledger, and rundown.
5//!
6//! A child of [`super`] rather than a sibling, because Rust's privacy is
7//! asymmetric: rundown here calls the parent's private `is_quiescent` and
8//! `drain_for_rundown`, which must stay up there so `Drop` can reach them too.
9
10use super::*;
11
12impl<T, X, K> IoRing<T, X, K> {
13    /// The version this ring was created at.
14    #[must_use]
15    pub fn version(&self) -> RingVersion {
16        self.version
17    }
18
19    /// Query this ring's current info via `GetIoRingInfo`.
20    ///
21    /// # Errors
22    ///
23    /// Returns any error from `GetIoRingInfo`.
24    pub fn info(&self) -> io::Result<RingInfo> {
25        let mut raw = IORING_INFO::default();
26        // SAFETY: `self.handle` is a live ring; `raw` is a valid out-pointer.
27        let hr = unsafe { GetIoRingInfo(self.handle, &raw mut raw) };
28        check(hr)?;
29        Ok(RingInfo {
30            version: RingVersion::from_raw(raw.IoRingVersion),
31            submission_queue_size: raw.SubmissionQueueSize,
32            completion_queue_size: raw.CompletionQueueSize,
33        })
34    }
35
36    /// Whether this ring supports `op`, from the capability set cached at
37    /// construction.
38    ///
39    /// Answers what the *kernel's* op table contains, not what this crate's
40    /// safe push surface reaches (M10.1, [`Op`]'s own docs list the mapping).
41    /// The two coincide for every op except [`Op::Nop`], which has no
42    /// [`crate::Batch`] method at all: a nop owns no buffer, so there is
43    /// nothing for the ring to hand back, and it is reachable only
44    /// through [`IoRing::push_raw`]. A `true` here therefore means "the
45    /// kernel would accept this op", not "a `Batch` method exists to push
46    /// it".
47    #[must_use]
48    pub fn supports(&self, op: Op) -> bool {
49        self.supported_ops.contains(op)
50    }
51
52    /// Overrides the cached capability set to exactly `ops`, for tests that
53    /// need a ring known to lack support for something.
54    ///
55    /// Every real host this crate has been tested against supports all seven
56    /// named ops, which is exactly why [`IoRing::supports`] and
57    /// [`Batch::require`](crate::Batch)'s use of it could not be told apart
58    /// from a constant `true` by any test that only ever asked a real ring:
59    /// the honest answer and the constant agree on every host available to
60    /// run the test. This seam constructs the disagreement instead of hoping
61    /// to find a host that has it.
62    ///
63    /// Not available outside `#[cfg(test)]`, for the same reason
64    /// [`Completion::synthetic`] is not: production code has no legitimate
65    /// reason to claim a capability the kernel did not actually report.
66    #[cfg(test)]
67    pub(crate) fn set_supported_ops_for_test(&mut self, ops: &[Op]) {
68        self.supported_ops = OpSupport(ops.iter().fold(0_u8, |mask, &op| {
69            let index = Op::ALL
70                .iter()
71                .position(|&candidate| candidate == op)
72                .expect("Op::ALL is exhaustive");
73            mask | (1 << index)
74        }));
75    }
76
77    /// An owned duplicate of this ring's completion event, so a caller can
78    /// wait on the ring alongside other handles without surrendering it
79    /// (M11.1, D-20).
80    ///
81    /// The ring creates the event, attaches it with
82    /// `SetIoRingCompletionEvent`, keeps ownership, and hands back a
83    /// duplicate. Closing the returned handle is therefore always safe: the
84    /// ring keeps signalling its own copy, and a `DuplicateHandle`'d event is
85    /// still signalled after the original is closed.
86    ///
87    /// **Idempotent.** Repeat calls return another duplicate of the *same*
88    /// event rather than attaching a new one, so two subsystems can each ask
89    /// without silently detaching the other's.
90    ///
91    /// This is not a third delivery architecture -- it is Model B with a
92    /// multiplexed wakeup source, changing only what the domain thread blocks
93    /// on, never who owns, submits, or drains ([`Batch::submit_and_wait`] is
94    /// still the single-source shape). Use it when ring I/O has to be waited
95    /// on alongside non-ring I/O, which
96    /// `IOSQE_FLAGS_DRAIN_PRECEDING_OPS` cannot order across.
97    ///
98    /// [`Batch::submit_and_wait`]: crate::Batch::submit_and_wait
99    ///
100    /// # The event is an edge, not a level
101    ///
102    /// **Measured, not inferred, and getting it wrong hangs rather than just
103    /// slowing down** (D-19). The event is signalled when the completion
104    /// queue transitions from **empty to non-empty**. It is *not* signalled
105    /// once per completion, and it is *not* level-triggered: eight
106    /// completions arriving at once into an empty queue produce exactly one
107    /// wakeup, and a completion arriving into an already-non-empty queue
108    /// produces none.
109    ///
110    /// Two rules follow, and they are contract rather than advice:
111    ///
112    /// 1. **Drain to empty before waiting again** -- [`IoRing::try_pop`]
113    ///    until it yields `None`, on *every* pass through a multiplexed wait
114    ///    loop, not only the pass where this handle signalled. A wait entered
115    ///    with entries still in the queue blocks until some later completion
116    ///    arrives *after* the queue has been emptied, which may be never.
117    ///    That is a lost-wakeup deadlock, not a latency wobble.
118    /// 2. **A wake with nothing to pop is normal.** It must not be treated as
119    ///    an error or as a spurious wakeup. This method deliberately produces
120    ///    one: the event is signalled once before it returns, so a caller who
121    ///    had already submitted work never misses a backlog that arrived
122    ///    before the event was attached.
123    ///
124    /// The event is auto-reset, and **exactly one waiter per ring** is
125    /// supported (D-21): the drain that restores the empty state, and so
126    /// re-arms the edge, must run to empty exactly once. Two threads waiting
127    /// on one ring's event cannot be made correct.
128    ///
129    /// # If you are arming a thread-pool wait on this handle
130    ///
131    /// `SetThreadpoolWait` documents that "you must re-register the event
132    /// with the wait object before signaling it each time to trigger the wait
133    /// callback". This method signals the event *before* it returns (rule 2
134    /// above), so by the time you have a handle to build a wait object
135    /// around, that setup signal has already happened -- in the order the
136    /// rule forbids, and so with no documented guarantee that your callback
137    /// runs. Combined with the edge rule above, a ring whose queue never
138    /// returns to empty has no second wakeup coming, so a setup signal that
139    /// does not reach your callback strands the backlog permanently rather
140    /// than merely delaying it.
141    ///
142    /// The remedy needs nothing this method does not already give you --
143    /// after arming the wait, signal your own duplicate yourself:    ///
144    /// ```no_run
145    /// # use windows_ioring_sys::IoRing;
146    /// # use std::os::windows::io::AsRawHandle;
147    /// # fn f(ring: &mut IoRing) -> std::io::Result<()> {
148    /// let event = ring.completion_event()?;
149    /// // ... build the wait object around `event`, then arm it ...
150    /// // Only now raise the wakeup, in the documented order. A wake with
151    /// // nothing to pop is normal (rule 2), so this is always safe.
152    /// unsafe { windows_sys::Win32::System::Threading::SetEvent(event.as_raw_handle()) };
153    /// # Ok(())
154    /// # }
155    /// ```
156    ///
157    #[cfg_attr(
158        feature = "threadpool",
159        doc = "[`EventDelivery`](crate::EventDelivery) already does this for you and"
160    )]
161    #[cfg_attr(
162        not(feature = "threadpool"),
163        doc = "`EventDelivery` (the default `threadpool` feature) already does this for you and"
164    )]
165    /// is the better answer if you do not need the handle itself.
166    ///
167    /// `examples/model_b_multiplexed.rs` is this whole shape worked end to
168    /// end -- a caller-owned ring waited on alongside a shutdown latch, with
169    /// the quiesce that shutdown-while-outstanding requires (M11.6).
170    ///
171    /// # Errors
172    ///
173    /// Returns [`io::ErrorKind::Unsupported`] if the running system does not
174    /// report `IORING_FEATURE_SET_COMPLETION_EVENT`; this crate refuses to
175    /// silently substitute a polling thread, since a caller who asked for an
176    /// event and got a spun-up thread has been told something false. Also
177    /// returns any error from `CreateEventW`,
178    /// `SetIoRingCompletionEvent`, `SetEvent`, or duplicating the handle.
179    pub fn completion_event(&mut self) -> io::Result<OwnedHandle> {
180        let event = self.attach_completion_event_unsignalled()?;
181        self.raise_setup_signal()?;
182        Ok(event)
183    }
184
185    /// Attach the ring's completion event *without* raising the setup signal,
186    /// reporting whether that signal is still owed.
187    ///
188    /// `SetThreadpoolWait` documents that "you must re-register the event with
189    /// the wait object before signaling it each time to trigger the wait
190    /// callback". A caller that is about to arm a threadpool wait on this
191    /// event therefore needs the attachment and the signal as two steps, so
192    /// that arming can be sequenced between them; handing back an
193    /// already-signalled event leaves that caller no way to obey the rule.
194    /// [`IoRing::completion_event`] is these two composed, for a caller who
195    /// does its own waiting and is not bound by that rule.
196    ///
197    /// The caller raises that signal **unconditionally**, whether or not this
198    /// call was the one that attached the event. The signal exists to make an
199    /// already-present backlog reachable, which is a property of the *waiter
200    /// about to arm* rather than of whoever attached the event first: a
201    /// caller that attaches, consumes the signal it raised, submits work, and
202    /// only then hands the ring to a waiter leaves a non-empty queue that the
203    /// edge rule (D-19) will never signal again. Owing the signal only to the
204    /// attaching call stranded exactly that backlog permanently, which is
205    /// `M26.12`.
206    pub(crate) fn attach_completion_event_unsignalled(&mut self) -> io::Result<OwnedHandle> {
207        // Already attached: hand back another duplicate rather than
208        // attaching a second event, which would silently detach the first
209        // (`SetIoRingCompletionEvent` replaces rather than adds). The
210        // capability was necessarily verified on the call that attached it.
211        if let Some(event) = &self.completion_event {
212            return event.try_clone();
213        }
214
215        if !capabilities()?.supports_completion_event {
216            return Err(io::Error::new(
217                io::ErrorKind::Unsupported,
218                "this system's IoRing does not report IORING_FEATURE_SET_COMPLETION_EVENT",
219            ));
220        }
221
222        // Auto-reset (manual_reset = FALSE) per D-21, initially unsignalled
223        // -- the deliberate setup signal is raised by `raise_setup_signal`,
224        // after the event is attached and owned, so it cannot be lost, and
225        // after any threadpool wait has been armed, so the arm-before-signal
226        // rule `SetThreadpoolWait` documents is obeyed.
227        // SAFETY: null attributes and name are documented defaults.
228        let raw = unsafe { CreateEventW(std::ptr::null(), 0, 0, std::ptr::null()) };
229        if raw.is_null() {
230            return Err(io::Error::last_os_error());
231        }
232        // SAFETY: `CreateEventW` just returned this handle and it is not
233        // owned anywhere else, so `OwnedHandle` is its sole owner from here.
234        let event = unsafe { OwnedHandle::from_raw_handle(raw) };
235
236        // SAFETY: `self.handle` is a live ring; `event` is a live event that
237        // this ring will own for the rest of its life once stored below.
238        let hr = unsafe {
239            crate::sys::set_completion_event(self.ring_id(), self.handle, event.as_raw_handle())
240        };
241        // On failure `event` drops here, closing a handle the ring never
242        // successfully referenced.
243        check(hr)?;
244
245        // Stored *before* the setup signal can be raised: from this point the
246        // ring owns the event, so no later failure can drop it and leave the
247        // ring signalling a closed (possibly recycled) handle.
248        self.completion_event.insert(event).try_clone()
249    }
250
251    /// Raise the one deliberate setup signal on the attached completion event.
252    ///
253    /// This is the single spurious wakeup the event's contract allows for: a
254    /// caller who submitted before attaching, **or who is about to start
255    /// waiting on an event someone else attached**, would otherwise never be
256    /// woken for that backlog, since the queue never returns to empty and so
257    /// never re-arms the edge (D-19). It is therefore raised for every such
258    /// caller rather than only for the one that performed the attachment --
259    /// see [`IoRing::attach_completion_event_unsignalled`] and `M26.12`.
260    ///
261    /// Separated from the attachment so that a caller arming a threadpool wait
262    /// can obey `SetThreadpoolWait`'s documented ordering -- register first,
263    /// signal second. Does nothing if no event is attached, which cannot
264    /// happen on the paths that call it.
265    pub(crate) fn raise_setup_signal(&self) -> io::Result<()> {
266        let Some(event) = &self.completion_event else {
267            return Ok(());
268        };
269        // SAFETY: `event` is a live event handle this ring owns.
270        if unsafe { SetEvent(event.as_raw_handle()) } == 0 {
271            return Err(io::Error::last_os_error());
272        }
273        // The setup signal is what a waiter attaching to a backlog depends on
274        // entirely, so M26.9's investigation needs to know it happened -- and
275        // that it happened on the ring's own handle rather than a duplicate
276        // handed to a caller, since only the former is what the kernel will go
277        // on signalling.
278        //
279        // Gated because `windows-threadpool-sys` is optional (D-22): this
280        // method is on the always-present path, unlike the delivery module,
281        // so an ungated reference breaks `--no-default-features`.
282        #[cfg(feature = "threadpool")]
283        windows_threadpool_sys::trace_record!(
284            "delivery",
285            "setup-signalled",
286            event.as_raw_handle() as usize,
287            self.accounting.outstanding()
288        );
289        Ok(())
290    }
291
292    /// Whether this ring supports a raw op code, including one this crate
293    /// does not yet name (D-7).
294    ///
295    /// Its reason to exist is an op outside [`Op`], which by definition this
296    /// ring's cached capability set was never probed for -- but passing a
297    /// named op's [`Op::code`] is equally in contract, and answers
298    /// identically to [`IoRing::supports`] (M10.1). The difference between
299    /// them is cost, not truth: `supports` is a bit test against the set
300    /// probed once at construction, this is an `IsIoRingOpSupported` call
301    /// every time.
302    ///
303    /// What a `true` here does *not* mean is that the op became pushable: an
304    /// op outside [`Op`] has no builder method whatever this answers, so
305    /// [`IoRing::push_raw`] remains the only route to one.
306    #[must_use]
307    pub fn supports_raw(&self, op_code: IORING_OP_CODE) -> bool {
308        // SAFETY: `self.handle` is a live ring.
309        unsafe { IsIoRingOpSupported(self.handle, op_code) != 0 }
310    }
311
312    /// How many file handles this ring has **reserved** for registration --
313    /// not how many are confirmed registered (M5.1, M10.3, D-31).
314    ///
315    /// The count advances the instant a `BuildIoRingRegisterFileHandles`
316    /// call queues, never when its completion is observed. Two consequences
317    /// a caller must not be surprised by:
318    ///
319    /// - it is already advanced before any completion has been popped, so it
320    ///   cannot be used to decide whether a registration has taken effect --
321    ///   claim the completion with
322    ///   [`crate::PendingFileRegistration::claim_if`] for that;
323    /// - it stays advanced after a registration whose completion reported
324    ///   *failure*, which is why such a registration cannot be retried on
325    ///   this ring ([`crate::Batch::register_files`]).
326    ///
327    /// Because a ring accepts at most one registration that assigns an
328    /// index, this is `0` until that registration is queued and its count
329    /// thereafter; there is no second registration for it to serve as a base
330    /// index for.
331    #[must_use]
332    pub fn registered_file_count(&self) -> u32 {
333        self.accounting.registered_file_count()
334    }
335
336    /// As [`IoRing::registered_file_count`], for registered buffers (M5.2) --
337    /// a **reserved** count, not a confirmed one, with the same two
338    /// consequences (M10.3, D-31).
339    #[must_use]
340    pub fn registered_buffer_count(&self) -> u32 {
341        self.accounting.registered_buffer_count()
342    }
343
344    /// Advance the registered-file base index by `count`, the instant a
345    /// `BuildIoRingRegisterFileHandles` call successfully queues (not once
346    /// its completion is observed).
347    ///
348    /// D-14 recorded this as an explicitly unverified assumption, since this
349    /// crate cannot know whether the kernel claims these `count` indices
350    /// synchronously at build time or only once the registration op runs.
351    /// D-31 (M10.3) dissolved that: the collision it guarded against needs a
352    /// *second* registration, and `Batch::register_files`/`register_buffers`
353    /// forbid one, so no later base index is ever derived from this count and
354    /// the kernel's actual timing has no observable consequence. What the
355    /// eager advance does still determine is the *meaning* of the public
356    /// accessors, which is why they document a reserved rather than a
357    /// confirmed count.
358    ///
359    /// D-32 did not answer this question, despite being adjacent to it: it
360    /// established when the kernel reads the `IORING_BUFFER_INFO` *array*,
361    /// which is a different thing from when it claims the *indices*. The
362    /// latter remains unmeasured, and dissolved rather than resolved.
363    pub(crate) fn reserve_registered_files(&mut self, count: u32) {
364        self.accounting.reserve_registered_files(count);
365    }
366
367    /// As [`IoRing::reserve_registered_files`], for registered buffers.
368    pub(crate) fn reserve_registered_buffers(&mut self, count: u32) {
369        self.accounting.reserve_registered_buffers(count);
370    }
371
372    /// Take ownership of the `IORING_BUFFER_INFO` array a
373    /// `BuildIoRingRegisterBuffers` call is about to be handed, keeping it
374    /// alive until the ring can prove the op has run, and hand back a stable
375    /// pointer to it (D-32).
376    ///
377    /// The kernel reads this array when the registration op *runs*, not when
378    /// the `Build*` call returns, so the caller must not build the SQE from a
379    /// temporary: store the array here first and pass the returned pointer.
380    /// For an empty array the pointer is dangling, as `Vec::as_ptr` is for
381    /// any empty `Vec`, and is never dereferenced: the count passed beside it
382    /// is zero.
383    ///
384    /// If the `Build*` call then fails, call
385    /// [`IoRing::release_unqueued_buffer_infos`]: nothing was queued, and a
386    /// retry needs to hold its own array.
387    pub(crate) fn hold_registered_buffer_infos(
388        &mut self,
389        infos: Vec<IORING_BUFFER_INFO>,
390    ) -> *const IORING_BUFFER_INFO {
391        debug_assert!(
392            self.late_read.buffer_infos.is_empty(),
393            "a ring accepts at most one buffer registration, so this must only be set once"
394        );
395        self.late_read.buffer_infos = infos;
396        // `Vec::as_ptr` is stable for as long as the `Vec` is neither moved
397        // out of nor reallocated; it lives in `self` and is never mutated
398        // again, and moving the `IoRing` itself moves only the `Vec` header,
399        // not its heap allocation.
400        self.late_read.buffer_infos.as_ptr()
401    }
402
403    /// As [`IoRing::hold_registered_buffer_infos`], for the handle array a
404    /// `BuildIoRingRegisterFileHandles` call is about to be handed.
405    ///
406    /// The kernel reads this one late too (D-32). Copied in rather than
407    /// borrowed from the caller, so a caller's slice only has to live for
408    /// the `register_files` call.
409    pub(crate) fn hold_registered_file_handles(
410        &mut self,
411        handles: Vec<*mut c_void>,
412    ) -> *const *mut c_void {
413        debug_assert!(
414            self.late_read.file_handles.is_empty(),
415            "a ring accepts at most one file registration, so this must only be set once"
416        );
417        self.late_read.file_handles = handles;
418        // Stable for the reason given in `hold_registered_buffer_infos`.
419        self.late_read.file_handles.as_ptr()
420    }
421
422    /// Keep the files a safe file registration queued for the rest of the
423    /// ring's life (M30.2, [D-81](../../DESIGN-NOTES.md#d-81)). `Drop`
424    /// releases them after `CloseIoRing` on a quiesced rundown, and leaks
425    /// them otherwise.
426    ///
427    /// Called only once the registration has queued: a refused one hands its
428    /// files back instead, since the kernel never saw their handles.
429    pub(crate) fn hold_registered_shared_files(
430        &mut self,
431        files: Vec<win_shared_os_owned_handle::SharedHandle>,
432    ) {
433        debug_assert!(
434            self.registered_files.is_empty(),
435            "a ring accepts at most one file registration, so this must only be set once"
436        );
437        *self.registered_files = files;
438    }
439
440    /// Release the buffer array held for a `BuildIoRingRegisterBuffers` call
441    /// that then failed.
442    ///
443    /// A failed `Build*` queues no SQE, so the kernel will never read the
444    /// array, and the documented answer to a full queue is to submit and
445    /// retry -- which holds a fresh array and would otherwise trip the
446    /// set-once assertion above.
447    pub(crate) fn release_unqueued_buffer_infos(&mut self) {
448        self.late_read.buffer_infos = Vec::new();
449    }
450
451    /// As [`IoRing::release_unqueued_buffer_infos`], for the handle array.
452    pub(crate) fn release_unqueued_file_handles(&mut self) {
453        self.late_read.file_handles = Vec::new();
454    }
455
456    /// How many operations this ring believes are still outstanding: minted
457    /// (via `reserve_user_data`) but not yet observed to have completed (via
458    /// `record_completion`, which retires only an identity this ring minted).
459    #[must_use]
460    pub fn outstanding(&self) -> usize {
461        self.accounting.outstanding()
462    }
463
464    /// Mint a fresh `UserData` identity for a new operation, and account for
465    /// it as outstanding until `record_completion` or `cancel_reservation` is
466    /// called with it.
467    ///
468    /// The identity is the whole of what the inventory needs to validate
469    /// a completion (D-4): unlike `windows-overlapped-io-sys`'s
470    /// `OperationId`, there is no separate storage address to pair it with,
471    /// because `UserData` is a value this crate chooses rather than one Win32
472    /// hands back.
473    ///
474    /// # Errors
475    ///
476    /// Returns an error rather than reusing an identity if the `usize` space
477    /// is ever exhausted, mirroring `windows-threadpool-sys`'s own
478    /// "exhausting the generation sequence fails rather than wraps."
479    pub(crate) fn reserve_user_data(&mut self) -> io::Result<usize> {
480        self.accounting.reserve_user_data()
481    }
482
483    /// Record that a completion carrying `user_data` has been observed (a real
484    /// `IORING_CQE` was popped), whether or not the ring was still holding
485    /// something for it. Returns whether `user_data` was outstanding; a
486    /// foreign or duplicate completion retires nothing.
487    pub(crate) fn record_completion(&mut self, user_data: usize) -> bool {
488        self.accounting.record_completion(user_data)
489    }
490
491    /// Release a reservation for an operation that was never actually
492    /// queued -- a `Build*` call failed synchronously, after
493    /// [`IoRing::reserve_user_data`] had already minted its identity.
494    ///
495    /// Distinct from [`IoRing::record_completion`]: that marks a real
496    /// `IORING_CQE` observed; this marks one that will never arrive because
497    /// the op never entered the queue, so it must not count against
498    /// [`IoRing::run_down`] either.
499    pub(crate) fn cancel_reservation(&mut self, user_data: usize) {
500        self.accounting.cancel_reservation(user_data);
501    }
502
503    /// This ring's ledger, for the crate's own minting paths (M24.2).
504    ///
505    /// Handed out rather than proxied so that an [`OperationId`] can be minted
506    /// from the bookkeeping alone -- which is what lets `token.rs`'s tests run
507    /// without a kernel ring, since minting is all they ever needed one for.
508    pub(crate) fn accounting_mut(&mut self) -> &mut Accounting {
509        &mut self.accounting
510    }
511
512    /// This ring's native handle, for `batch.rs`'s `Build*`/`Submit` calls.
513    pub(crate) fn raw_handle(&self) -> *mut c_void {
514        self.handle
515    }
516
517    /// This ring's own identity, for stamping onto every [`crate::OperationId`]/
518    /// registration it mints and checking against on use (PR #20 review
519    /// response); see [`RingId`].
520    pub(crate) fn ring_id(&self) -> RingId {
521        self.accounting.ring_id()
522    }
523
524    /// Queue a raw, not-yet-wrapped SQE via a caller-supplied `Build*` call
525    /// (M3.5, D-7).
526    ///
527    /// `build` receives this ring's native handle and a freshly reserved
528    /// `UserData` value, and must call exactly one `BuildIoRing*` function
529    /// with them, returning its `HRESULT`. On success the `UserData` is
530    /// returned so the caller can match it against a later [`Completion`]
531    /// popped by [`IoRing::try_pop`]; on failure the reservation is
532    /// released, since the op was never actually queued.
533    ///
534    /// **Matching by hand is correct here, and only here.** This seam creates
535    /// no inventory entry -- it cannot, since it does not know what the
536    /// caller's `build` closure queued or what it needs held -- so a pop for
537    /// this operation reports `None` for what the ring was holding. That is
538    /// the legitimate half of [D-75](../../DESIGN-NOTES.md#d-75)'s two causes,
539    /// and choosing this seam is what makes it legitimate. Every other push
540    /// hands its payload to the ring and gets it back from the pop.
541    ///
542    /// # Errors
543    ///
544    /// Returns any error `build`'s `HRESULT` reports.
545    ///
546    /// # Safety
547    ///
548    /// `build` must queue an SQE for a *self-contained* op: everything the
549    /// kernel reads or writes for it must stay valid until the
550    /// corresponding completion is observed, and this crate cannot verify
551    /// what `build` does with the handle it is given. This is the same
552    /// framing as `windows-overlapped-io-sys`'s raw `ioctl` seam
553    /// (`device.rs`): the mechanics of building an SQE need nothing unsafe,
554    /// but this crate cannot audit an arbitrary `Build*` call, so the seam
555    /// itself is unsafe.
556    ///
557    /// `build` must also queue its SQE with **exactly the `user_data` it was
558    /// handed**, and queue only one. A completion carrying any other identity
559    /// is one this ring never minted, and the pop that receives it panics
560    /// ([D-79](../../DESIGN-NOTES.md#d-79)).
561    pub unsafe fn push_raw(
562        &mut self,
563        build: impl FnOnce(*mut c_void, usize) -> windows_sys::core::HRESULT,
564    ) -> io::Result<usize>
565    where
566        K: AllowsRaw,
567    {
568        let user_data = self.reserve_user_data()?;
569        let hr = build(self.handle, user_data);
570        if let Err(error) = check(hr) {
571            self.cancel_reservation(user_data);
572            return Err(error);
573        }
574        Ok(user_data)
575    }
576
577    /// Block until every outstanding operation has completed, so
578    /// `CloseIoRing` never runs while the kernel might still be touching a
579    /// buffer the ring holds for an in-flight operation.
580    ///
581    /// Waits in short, rechecked steps via `SubmitIoRing`'s own wait -- with
582    /// zero new entries queued, its only effect is to block for up to
583    /// `RUN_DOWN_POLL_MS` and reap whatever is already outstanding -- rather
584    /// than one unbounded call. This does not interpret what it pops: each
585    /// popped entry's payload is dropped, which is sound because its
586    /// completion is the proof the kernel has finished with it. Idempotent: calling it
587    /// again once `outstanding() == 0` is a no-op.
588    ///
589    /// # A poll that expires is not a failure
590    ///
591    /// Each poll blocks for `RUN_DOWN_POLL_MS` and then reports
592    /// `ERROR_TIMEOUT` if nothing finished in that window, which is the
593    /// ordinary outcome for any operation slower than 50ms. Treating that as
594    /// an error -- which this did until M21.6 -- made `run_down` return `Err`
595    /// with the operation still outstanding, so `Drop` asserted and then
596    /// called `CloseIoRing` anyway: exactly the "the kernel may still be
597    /// writing through a buffer the ring holds" hazard this function exists to
598    /// prevent.
599    ///
600    /// This loop therefore has no overall bound, and that is deliberate.
601    /// **Every SQE that successfully queues produces exactly one completion**
602    /// (M10.2), so it terminates. Blocking until that holds is the safe
603    /// failure mode; closing the ring early is not.
604    ///
605    /// # Choosing an unbounded wait is the caller's to make
606    ///
607    /// This spelling waits until rundown finishes, however long that takes,
608    /// and calling it is how a caller elects that. A caller who wants to
609    /// decide for themselves -- a deadline, a backoff, a number of attempts
610    /// before giving up -- calls [`IoRing::run_down_within`] instead and owns
611    /// the policy entirely. **This crate does not implement retry policy**
612    /// (M26.8): it supplies a bounded primitive and reports what happened.
613    ///
614    /// Note the difference from [`IoRing::pop_within`], which also waits in
615    /// segments: there the segments sit *inside a period the caller supplied*,
616    /// which is a bounded wait implemented properly rather than a policy. This
617    /// method had segments and no such period, which is what made it the one
618    /// waiting API in this crate shaped wrongly.
619    ///
620    /// # Errors
621    ///
622    /// Returns any error from `SubmitIoRing` other than an expired wait, or
623    /// from `PopIoRingCompletion`. **An error leaves the ring resumable**: see
624    /// [`IoRing::run_down_within`] for what is guaranteed about the operations
625    /// still queued.
626    ///
627    /// # Panics
628    ///
629    /// As [`IoRing::try_pop`], on a completion for an identity not in flight.
630    pub fn run_down(&mut self) -> io::Result<()> {
631        while !self.run_down_within(Duration::MAX)? {}
632        Ok(())
633    }
634
635    /// Run down for at most `bound`, reporting whether it finished.
636    ///
637    /// `Ok(true)` means nothing is outstanding and the ring is safe to drop.
638    /// `Ok(false)` means the bound elapsed with work still in flight -- call
639    /// again when your own policy says to. [`IoRing::outstanding`] says how
640    /// much is left.
641    ///
642    /// # Why this exists, and why it returns rather than retries
643    ///
644    /// Rundown can fail for a reason that a later attempt would survive, and
645    /// **deciding whether to make that attempt is not this crate's business**.
646    /// A caller running under a deadline, a supervisor with a backoff, and a
647    /// test that wants to fail fast all want different answers, and a policy
648    /// baked in here would be wrong for two of the three. So this waits for
649    /// exactly as long as it is told and then reports.
650    ///
651    /// # What an error guarantees, which is what makes retrying safe
652    ///
653    /// `SubmitIoRing` documents that *"If this function returns an error other
654    /// than IORING_E_WAIT_TIMEOUT, then all entries remain in the submission
655    /// queue."* So a failure here has **not** lost the operations and has not
656    /// rewound them ([D-5](../../DESIGN-NOTES.md#d-5)); they are still ring state,
657    /// a later submit is what runs them, and their buffers must stay alive
658    /// until they complete.
659    ///
660    /// The consequence worth stating plainly: after an `Err`, **do not drop
661    /// this ring**. Dropping it closes a ring the kernel may still write
662    /// through, which is the hazard rundown exists to prevent. Call again.
663    ///
664    /// # Errors
665    ///
666    /// Returns any error from `SubmitIoRing` other than an expired wait, or
667    /// from `PopIoRingCompletion`.
668    ///
669    /// # Panics
670    ///
671    /// As [`IoRing::try_pop`], on a completion for an identity not in flight.
672    pub fn run_down_within(&mut self, bound: Duration) -> io::Result<bool> {
673        let deadline = Instant::now().checked_add(bound);
674        loop {
675            if self.is_quiescent() {
676                return Ok(true);
677            }
678            // `is_quiescent` waits on IDENTITIES, not a count. With A and B
679            // outstanding, a foreign CQE plus A's real one retires only A, so
680            // this keeps waiting for B -- and B's will arrive, because every SQE
681            // that queues produces exactly one completion (M10.2). That is the
682            // invariant that already justifies this loop having no overall
683            // bound, and it holds for raw pushes, which stow nothing, exactly as
684            // for owned ones.
685            // `checked_add` rather than `+`, for the reason `pop_within_with`
686            // records: `Instant + Duration` panics on overflow, so
687            // `Duration::MAX` -- the honest spelling of "no deadline" -- would
688            // take down the process. A deadline the clock cannot represent is
689            // one that never arrives, which is what was asked for.
690            let remaining = match deadline {
691                Some(deadline) => deadline.saturating_duration_since(Instant::now()),
692                None => Duration::MAX,
693            };
694            if remaining.is_zero() {
695                return Ok(false);
696            }
697            // Reap before waiting, once the bound has said to wait at all: `SubmitIoRing` with a
698            // wait reports `IORING_E_COMPLETION_QUEUE_TOO_FULL` when the completion queue holds as
699            // many completions as it can, whatever it was asked to wait for, and an entry that
700            // failed to submit for the same want of room is still queued behind them. Waiting first
701            // fails the rundown of exactly the ring that most needs one, with the room it wants one
702            // pop away (M39.1; the same rule the completion event's edge states, D-19: drain, then
703            // wait).
704            self.drain_for_rundown()?;
705            if self.is_quiescent() {
706                return Ok(true);
707            }
708            // Segments sit inside the caller's period, never outside it: the
709            // poll is the shorter of the rundown step and what is left.
710            let poll_ms = u32::try_from(remaining.as_millis())
711                .unwrap_or(u32::MAX)
712                .clamp(1, RUN_DOWN_POLL_MS);
713            let mut submitted = 0_u32;
714            // SAFETY: `self.handle` is a live ring; valid out-pointer. Zero
715            // new SQEs are queued -- this call's only purpose is to wait for
716            // and reap already-outstanding completions.
717            let hr = unsafe {
718                crate::sys::submit(self.ring_id(), self.handle, 1, poll_ms, &raw mut submitted)
719            };
720            wait_outcome(hr)?;
721            self.drain_for_rundown()?;
722        }
723    }
724}