Skip to main content

win_durable_ioring/
dioring.rs

1// Copyright (c) 2026 Mike Grier
2//! dioring: the contract's implementation over `windows-ioring-sys` (DI-D-49, DI-D-53).
3//!
4//! An instance is a [`DurableIoCore`] over a [`WindowsBackend`], joined by an [`Engine`] that its
5//! delivery drains whenever the ring's completion event says to: the core decides, the backend runs
6//! the I/O, and the driver here joins them to the consumer's calls. **Delivery is chosen at
7//! construction** ([`DeliveryMode`], DI-D-55): drained on the thread pool, or by the consumer's own
8//! thread in [`Dioring::pump`]. Every path -- a consumer's call, a drain, a provider's answer, a
9//! lineage handle's release -- takes the engine's one lock, so the queue's order is the order
10//! completions were recorded in, whichever thread recorded them.
11//!
12//! **The readiness event** is set whenever a path made entries poppable where there were none, after
13//! it releases the lock, so they are poppable before it is set (DI-D-28).
14//!
15//! **The consumer's provider** is called at the end of the consumer's own calls, with the lock
16//! released, so it may answer inside its call (DI-D-44). A request a pool drain, an answer or a
17//! release makes waits in the engine for the consumer's next call, normally the `pop` that follows.
18//!
19//! **Ending** -- `close()` or drop -- stops delivery and takes the engine out of its slot, so an
20//! answer or a release that comes later finds nothing; drops the provider, so a completion it keeps
21//! is answered as abandoned; waits until every completion made has been answered or dropped
22//! (DI-D-25); then closes the core and drops the backend, whose ring runs down every operation still
23//! in flight. An internal inconsistency is not looked for here: it is panicked on where it is found
24//! (DI-D-46).
25
26use std::io;
27use std::os::windows::io::OwnedHandle;
28use std::sync::{Arc, Weak};
29use std::time::Duration;
30
31use win_shared_os_owned_handle::SharedHandle;
32use win_sync_sys::{Event, ResetMode};
33use win_time_sys::{Clock, InterruptClock, InterruptTime, Steady, TimePoint};
34use windows_ioring_sys::{
35    Batch, Completion, IoBuf, IoBufMut, IoRing, PushRefused, RegisteredBuffers, RegisteredFiles,
36    RegisteredSpan,
37};
38use windows_threadpool_sys::callback_env::CallbackEnviron;
39
40use crate::windows_backend::pool::{PoolDelivery, PoolEngine};
41use crate::windows_backend::signalled::SignalledDelivery;
42use crate::windows_backend::{
43    Construction, Win32FileTarget, WindowsBackend, WindowsBackendError, WindowsBackendRefusal,
44    WindowsBackendSetup,
45};
46use durable_ioring::DurableIoCore;
47use durable_ioring::IoTarget;
48use durable_ioring::StampClock;
49use durable_ioring::backend::{IoBackend, RegisteredRegion, SubmissionData};
50use durable_ioring::driver::Home;
51use durable_ioring::driver::PumpedDelivery;
52use durable_ioring::driver::first_repeat;
53use durable_ioring::driver::{Answer, DomainCall};
54use durable_ioring::driver::{Engine, EngineGuard};
55use durable_ioring::{
56    AddTargetError, DurabilityRequest, EndLineageError, EndLineageRefusal, Epoch, EpochState,
57    FailureInfo, FlushDomain, ImportScope, Imported, IoTargetKey, Leftovers, LineageInfo,
58    ReadOptions, RemoveTargetError, Resolution, ResolveError, RetireLineageError,
59    RetireLineageRefusal, SealError, Tag, UnknownLineage, WriteOptions,
60};
61use durable_ioring::{DiagnosticSink, NoDiagnostics};
62use durable_ioring::{DioringIds, FailureId, FailureToken, Lineage, LineageHandle};
63use durable_ioring::{
64    DomainCompletion, DomainRequest, DurabilityProvider, FlushRequest, ProviderPart,
65    ProviderRequest,
66};
67use durable_ioring::{DurableRing, EntryOf, EpochId, PushResult, RegisteredBufferRing};
68
69mod delivery;
70mod hub;
71
72use delivery::Delivery;
73use hub::Hub;
74
75// `pub(crate)` so other modules' tests can build an instance with the helpers here.
76#[cfg(test)]
77pub(crate) mod tests;
78
79/// A clock dioring can stamp with: a [`StampClock`] whose readings are interrupt time, its one time
80/// base (DI-D-37), and never decrease, and that can be held under the lock pool threads take
81/// (DI-D-38). [`InterruptStamps`] is the default; a test may give an instance its own mock, which
82/// takes on `StampClock`'s promise by implementing it, and a `win-time-sys` clock on interrupt
83/// time is given as [`FromClock`]. Implemented for every type that meets those bounds.
84pub trait TimeBase: StampClock<Instant = WinInstant> {}
85
86impl<K: StampClock<Instant = WinInstant>> TimeBase for K {}
87
88/// The time type the Windows driver's stamps are read in: interrupt time, its one time base
89/// (DI-D-37).
90pub type WinInstant = TimePoint<InterruptTime>;
91
92/// dioring's identities over the Windows driver's stamps (DI-D-50): what a consumer names its
93/// [`Dioring`]'s identities by.
94pub type WindowsIds<E> = DioringIds<E, WinInstant>;
95
96/// Interrupt time, read through `win-time-sys`' `InterruptClock`: the default clock a [`Dioring`]
97/// stamps with (DI-D-37). Its readings never decrease, which is `StampClock`'s promise.
98#[derive(Clone, Copy, Debug, Default)]
99pub struct InterruptStamps;
100
101impl StampClock for InterruptStamps {
102    type Instant = WinInstant;
103
104    fn now(&self) -> WinInstant {
105        Clock::now(&InterruptClock)
106    }
107}
108
109/// A `win-time-sys` clock on interrupt time, given to dioring as the clock it stamps with: the
110/// clock must keep [`Steady`]'s promise that its readings never decrease, which it states by
111/// implementing it. A `win-time-sys` clock cannot be a [`StampClock`] itself, because the trait is
112/// another crate's.
113#[derive(Clone, Copy, Debug, Default)]
114pub struct FromClock<K>(pub K);
115
116impl<K: Clock<Timeline = InterruptTime> + Steady + Send + 'static> StampClock for FromClock<K> {
117    type Instant = WinInstant;
118
119    fn now(&self) -> WinInstant {
120        Clock::now(&self.0)
121    }
122}
123
124/// How long construction waits for each registration's completion. A registration is a
125/// bookkeeping operation the kernel completes at once; the bound exists so a ring that never
126/// answers fails construction instead of hanging it.
127pub(crate) const REGISTRATION_BOUND: Duration = Duration::from_secs(30);
128
129/// What a dioring instance is built with. Both registrations are the ring's only ones (one of
130/// each per ring), so they are made here or never.
131pub struct Setup<'env, 'pool, E: EpochId + 'static, R> {
132    /// The ring's submission queue size, as `windows-ioring-sys` takes it.
133    pub submission_queue_size: u32,
134    /// The ring's completion queue size, as `windows-ioring-sys` takes it.
135    pub completion_queue_size: u32,
136    /// Registered with the ring, which holds their files for its life. A target added later is not
137    /// registered.
138    pub files: Vec<TargetSetup>,
139    /// Already allocated and placed by the consumer; dioring neither allocates nor places them.
140    /// Empty registers nothing.
141    pub buffers: Vec<R>,
142    /// The consumer's durability provider, if any. `None` leaves every domain to the built-in
143    /// default.
144    pub provider: Option<Box<dyn DurabilityProvider<WindowsIds<E>>>>,
145    /// Where the instance's diagnostics go (DI-D-51). `None` is the driver's default, which is
146    /// [`NoDiagnostics`] until `DI-12.1` makes it ETW.
147    pub diagnostics: Option<Arc<dyn DiagnosticSink>>,
148    /// How the instance's completions are drained (DI-D-52, DI-D-55).
149    pub delivery: DeliveryMode<'env, 'pool>,
150}
151
152/// How an instance's completions are drained: by the thread pool, or by the consumer's own thread,
153/// which the pool may tell. Chosen once, as the instance is built, and not changed after.
154pub enum DeliveryMode<'env, 'pool> {
155    /// Drained on a pool thread whenever the ring's completion event says to, and the consumer
156    /// told through [`DurableRing::readiness`]. Holds the thread pool, and the priority within it,
157    /// that delivery runs on -- a NUMA node's own pool, for one; `None` is the process's default
158    /// pool. Read once, as the instance is built.
159    Pool(Option<&'env mut CallbackEnviron<'pool>>),
160    /// Drained by the consumer's own thread, which calls [`Dioring::pump`]: no thread of the
161    /// instance's own, and none of the pool's. Nothing is drained until it does. Its pump waits on
162    /// the ring's own completion event, a handle to which [`Dioring::completion_event`] offers
163    /// the consumer, to wait on beside its own handles before it pumps.
164    Pumped,
165    /// Drained by the consumer's own thread, as [`Pumped`](Self::Pumped), which the thread pool
166    /// tells: it sets an event of the instance's own, [`Dioring::completion_signal`], whenever
167    /// completions may be waiting, and the consumer waits on that beside its own handles and then
168    /// calls [`Dioring::pump`]. A thread-pool wait holds no thread while it waits; its callback only
169    /// sets the event. Holds the thread pool, and the priority within it, that callback runs on,
170    /// as [`Pool`](Self::Pool)'s does.
171    Signalled(Option<&'env mut CallbackEnviron<'pool>>),
172}
173
174/// One target given at construction.
175#[derive(Debug)]
176pub struct TargetSetup {
177    /// The consumer's key for it, unique within the instance.
178    pub key: IoTargetKey,
179    /// The target: its file, and what it states, flush domains among it.
180    pub target: Win32FileTarget,
181}
182
183/// Why construction failed.
184#[derive(Debug)]
185pub enum SetupRefusal {
186    /// Two targets share a key.
187    DuplicateKey(IoTargetKey),
188    /// The provider named a domain twice.
189    DuplicateProviderDomain(FlushDomain),
190    /// Creating the ring, making a registration, or wiring the ring's delivery failed.
191    Ring(io::Error),
192}
193
194/// A failed construction hands back what it was given. `buffers` is `None` only once the buffers
195/// have been handed to the kernel and could not be shown to have come back: when the kernel's
196/// registration completion itself failed (`windows-ioring-sys` drops them then), when no
197/// completion arrived (the ring crate leaks them rather than free memory the kernel may hold), or
198/// when the registration succeeded and wiring the ring's delivery then failed (the registration
199/// holds them, and has no way to give them back).
200///
201/// That last failure is not reached by this crate's tests: wiring delivery fails where the system
202/// does not support a ring completion event, or the thread pool cannot create a wait.
203pub struct SetupError<E: EpochId + 'static, R> {
204    /// Why construction failed.
205    pub reason: SetupRefusal,
206    /// The targets, in the order given.
207    pub files: Vec<TargetSetup>,
208    /// The buffers, unless the kernel was given them; see above.
209    pub buffers: Option<Vec<R>>,
210    /// The provider, if one was given.
211    pub provider: Option<Box<dyn DurabilityProvider<WindowsIds<E>>>>,
212}
213
214impl<E: EpochId + 'static, R> std::fmt::Debug for SetupError<E, R> {
215    // By hand, so no bound is placed on `R`: a buffer type need not be `Debug` for `.expect(..)`
216    // to compile.
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        f.debug_struct("SetupError")
219            .field("reason", &self.reason)
220            .field("files", &self.files)
221            .field("buffers", &self.buffers.as_ref().map(Vec::len))
222            .field("provider", &self.provider)
223            .finish()
224    }
225}
226
227/// dioring: `B` is the owned buffer type, `E` the epoch-id type, `C` the consumer's
228/// per-operation context, `R` the registered-buffer type, and `K` the clock it stamps failures with
229/// (a [`TimeBase`]; [`InterruptStamps`] unless built with [`Dioring::with_clock`]).
230///
231/// The instance's core and its Windows backend are drained on the thread pool (DI-D-53), so the
232/// buffer, epoch-id, context and registered-buffer types cross to pool threads and must be `Send`;
233/// the epoch-id type must also be `Sync`, because a failure's suspect set is shared. An owned write
234/// needs only `B: IoBuf`, so a write-only type serves; an owned read needs `B: IoBufMut`
235/// (DI-D-17).
236pub struct Dioring<
237    B: IoBuf,
238    E: EpochId + 'static = u64,
239    C = (),
240    R: IoBufMut = Vec<u8>,
241    K: StampClock = InterruptStamps,
242> {
243    /// The core and its backend, and how they are drained; `None` once the instance has ended.
244    pub(crate) delivery: Option<Delivery<E, B, C, K, R>>,
245    pub(crate) hub: Arc<Hub<E, B, C, K, R>>,
246    pub(crate) provider: Option<Box<dyn DurabilityProvider<WindowsIds<E>>>>,
247    /// A copy of the default lineage's handle, so the default lineage lives as long as the
248    /// instance does (DI-D-40).
249    pub(crate) default: LineageHandle,
250}
251
252impl<B, E, C, R> Dioring<B, E, C, R>
253where
254    B: IoBuf + Send + 'static,
255    E: EpochId + Send + Sync + 'static,
256    C: Send + 'static,
257    R: IoBufMut + Send + 'static,
258{
259    /// Build an instance, make the ring's registrations, and wire its delivery. Blocks until both
260    /// registrations have completed. Returns the instance with a handle to its default lineage
261    /// (DI-D-40). Failures are stamped with [`InterruptStamps`]; see [`Dioring::with_clock`] for
262    /// another clock.
263    ///
264    /// # Errors
265    ///
266    /// A [`SetupError`] handing back what it was given, for a duplicate target key, a provider
267    /// domain named twice, or a ring that could not be created, make a registration, or be wired
268    /// to deliver its completions. The checks that need no ring run first, so a refusal for
269    /// either of the first two creates nothing.
270    pub fn new(setup: Setup<'_, '_, E, R>) -> Result<(Self, LineageHandle), SetupError<E, R>> {
271        Self::with_clock(setup, InterruptStamps)
272    }
273}
274
275impl<B, E, C, R, K> Dioring<B, E, C, R, K>
276where
277    B: IoBuf + Send + 'static,
278    E: EpochId + Send + Sync + 'static,
279    C: Send + 'static,
280    R: IoBufMut + Send + 'static,
281    K: TimeBase,
282{
283    /// [`Dioring::new`], stamping failures with `clock` (DI-D-38): a test's mock, for one, which
284    /// must keep [`StampClock`]'s promise that its readings never decrease.
285    ///
286    /// # Errors
287    ///
288    /// As [`Dioring::new`].
289    pub fn with_clock(
290        setup: Setup<'_, '_, E, R>,
291        clock: K,
292    ) -> Result<(Self, LineageHandle), SetupError<E, R>> {
293        Self::with_construction(setup, clock, Construction::default())
294    }
295
296    /// [`with_clock`](Self::with_clock), as `construction` says (DI-7.2).
297    ///
298    /// # Errors
299    ///
300    /// As [`Dioring::new`].
301    pub(crate) fn with_construction(
302        setup: Setup<'_, '_, E, R>,
303        clock: K,
304        construction: Construction<B>,
305    ) -> Result<(Self, LineageHandle), SetupError<E, R>> {
306        let Setup {
307            submission_queue_size,
308            completion_queue_size,
309            files,
310            buffers,
311            provider,
312            diagnostics,
313            delivery,
314        } = setup;
315        let diagnostics = diagnostics.unwrap_or_else(|| Arc::new(NoDiagnostics));
316
317        if let Some(key) = first_repeat(files.iter().map(|f| f.key)) {
318            return Err(SetupError {
319                reason: SetupRefusal::DuplicateKey(key),
320                files,
321                buffers: Some(buffers),
322                provider,
323            });
324        }
325        let provider_domains = provider.as_ref().map(|p| p.domains()).unwrap_or_default();
326        if let Some(domain) = first_repeat(provider_domains.iter().cloned()) {
327            return Err(SetupError {
328                reason: SetupRefusal::DuplicateProviderDomain(domain),
329                files,
330                buffers: Some(buffers),
331                provider,
332            });
333        }
334
335        let signals = Event::new(ResetMode::Auto, false)
336            .and_then(|readiness| Ok((readiness.try_clone()?, readiness)));
337        let (ready, readiness) = match signals {
338            Ok(signals) => signals,
339            Err(error) => {
340                return Err(SetupError {
341                    reason: SetupRefusal::Ring(error),
342                    files,
343                    buffers: Some(buffers),
344                    provider,
345                });
346            }
347        };
348        let mut core = if provider.is_some() {
349            DurableIoCore::with_provider(clock, Arc::clone(&diagnostics), provider_domains)
350                .expect("a domain named twice was refused above")
351        } else {
352            DurableIoCore::new(clock, Arc::clone(&diagnostics))
353        };
354        let keys: Vec<IoTargetKey> = files.iter().map(|f| f.key).collect();
355        for file in &files {
356            core.add_target(file.key, file.target.describe())
357                .expect("the keys were checked unique above");
358        }
359        let targets = files
360            .into_iter()
361            .map(|TargetSetup { key, target }| (key, target))
362            .collect();
363        let backend = match WindowsBackend::construct(
364            WindowsBackendSetup {
365                submission_queue_size,
366                completion_queue_size,
367                targets,
368                buffers,
369            },
370            construction,
371        ) {
372            Ok(backend) => backend,
373            Err(WindowsBackendError {
374                reason,
375                targets,
376                buffers,
377            }) => {
378                let reason = match reason {
379                    WindowsBackendRefusal::DuplicateKey(key) => SetupRefusal::DuplicateKey(key),
380                    WindowsBackendRefusal::Ring(error) => SetupRefusal::Ring(error),
381                };
382                return Err(SetupError {
383                    reason,
384                    files: setups(targets),
385                    buffers,
386                    provider,
387                });
388            }
389        };
390
391        let default_lineage = core.default_lineage();
392        let on_ready = move || {
393            // Setting an event this instance created, with full access, does not fail.
394            let set = ready.set();
395            debug_assert!(set.is_ok(), "setting the readiness event failed: {set:?}");
396        };
397        let engine = Engine::new(core, backend);
398        let delivered = match delivery {
399            DeliveryMode::Pool(environment) => {
400                PoolDelivery::new(engine, on_ready, environment).map(Delivery::Pool)
401            }
402            // Nothing sets the signal the pool's wake would: entries are made poppable by the
403            // pump, which sets the readiness event itself, as every other path does.
404            DeliveryMode::Pumped => PumpedDelivery::new(engine).map(Delivery::Pumped),
405            DeliveryMode::Signalled(environment) => {
406                SignalledDelivery::new(engine, environment).map(Delivery::Signalled)
407            }
408        };
409        let delivery = match delivered {
410            Ok(delivery) => delivery,
411            Err((error, engine)) => {
412                let mut backend = engine.backend;
413                let files = keys
414                    .into_iter()
415                    .filter_map(|key| {
416                        backend
417                            .detach(key)
418                            .map(|target| TargetSetup { key, target })
419                    })
420                    .collect();
421                return Err(SetupError {
422                    reason: SetupRefusal::Ring(error),
423                    files,
424                    buffers: None,
425                    provider,
426                });
427            }
428        };
429        let hub = Arc::new(Hub::new(
430            delivery.shared(),
431            readiness,
432            default_lineage.instance,
433            diagnostics,
434        ));
435        let default = LineageHandle::mint(default_lineage, home(&hub));
436        let handle = default.clone();
437        Ok((
438            Self {
439                delivery: Some(delivery),
440                hub,
441                provider,
442                default,
443            },
444            handle,
445        ))
446    }
447}
448
449/// Targets handed back as they were given.
450fn setups(targets: Vec<(IoTargetKey, Win32FileTarget)>) -> Vec<TargetSetup> {
451    targets
452        .into_iter()
453        .map(|(key, target)| TargetSetup { key, target })
454        .collect()
455}
456
457/// Where an instance's lineage handles report their release.
458fn home<T: Home + 'static>(hub: &Arc<T>) -> Weak<dyn Home> {
459    let home: Weak<T> = Arc::downgrade(hub);
460    home
461}
462
463/// Where an instance's provider completions answer.
464fn answers<T: Answer + 'static>(hub: &Arc<T>) -> Weak<dyn Answer> {
465    let answers: Weak<T> = Arc::downgrade(hub);
466    answers
467}
468
469impl<B, E, C, R, K> DurableRing for Dioring<B, E, C, R, K>
470where
471    B: IoBuf + Send + 'static,
472    E: EpochId + Send + Sync + 'static,
473    C: Send + 'static,
474    R: IoBufMut + Send + 'static,
475    K: TimeBase,
476{
477    type Ids = WindowsIds<E>;
478    type Buffer = B;
479    type Context = C;
480    type Readiness = Event;
481    type Target = Win32FileTarget;
482
483    fn readiness(&mut self) -> io::Result<Event> {
484        self.hub.readiness()
485    }
486
487    fn add_target(
488        &mut self,
489        key: IoTargetKey,
490        target: Win32FileTarget,
491    ) -> Result<(), AddTargetError<Win32FileTarget>> {
492        let mut engine = self.engine();
493        if engine.core.target(key).is_some() || engine.backend.target(key).is_some() {
494            return Err(AddTargetError { key, target });
495        }
496        engine
497            .core
498            .add_target(key, target.describe())
499            .expect("the key is free, checked above");
500        engine
501            .backend
502            .attach(key, target)
503            .expect("the key is free, checked above");
504        Ok(())
505    }
506
507    fn remove_target(
508        &mut self,
509        key: IoTargetKey,
510    ) -> Result<Win32FileTarget, RemoveTargetError<Self::Ids>> {
511        let mut engine = self.engine();
512        engine.core.remove_target(key)?;
513        Ok(engine
514            .backend
515            .detach(key)
516            .expect("a target the core decided for is attached"))
517    }
518
519    fn write_with(
520        &mut self,
521        file: IoTargetKey,
522        offset: u64,
523        buffer: B,
524        tag: Tag<'_, Self::Ids>,
525        context: C,
526        options: WriteOptions<Self::Ids>,
527    ) -> PushResult<Self>
528    where
529        B: IoBuf,
530    {
531        let (pushed, attention) = {
532            let mut engine = self.engine();
533            let pushed = engine.write(
534                file,
535                offset,
536                SubmissionData::Owned(buffer),
537                tag.epoch(),
538                context,
539                options,
540            );
541            (pushed, engine.take_attention())
542        };
543        self.attend(attention);
544        pushed
545    }
546
547    fn read_with(
548        &mut self,
549        file: IoTargetKey,
550        offset: u64,
551        buffer: B,
552        context: C,
553        options: ReadOptions<Self::Ids>,
554    ) -> PushResult<Self>
555    where
556        B: IoBufMut,
557    {
558        let (pushed, attention) = {
559            let mut engine = self.engine();
560            // Here, where `B: IoBufMut` holds, the backend is given the push an owned read needs
561            // (DI-D-17).
562            engine.backend.enable_owned_reads();
563            let pushed = engine.read(
564                file,
565                offset,
566                SubmissionData::Owned(buffer),
567                context,
568                options,
569            );
570            (pushed, engine.take_attention())
571        };
572        self.attend(attention);
573        pushed
574    }
575
576    fn pop(&mut self) -> io::Result<Option<EntryOf<Self>>> {
577        // A push is submitted as it is made, by the engine's doorbell, so what is left to submit
578        // here is what a failed one left queued, and the driver told the consumer so (DI-7+.2). It
579        // is reported only when there is no entry to return instead: an entry is progress, and
580        // the submission is tried again at the next pop. A fault the backend gave when its
581        // completions were read is reported the same way, once: the consumer's remedy for either
582        // that stays is to drop the instance.
583        //
584        // A submission of the core's own the backend refused needs nothing here: its cause is
585        // cleared by a submit or a drain, and either way completions are still to come, whose
586        // actions the pool drain re-emits it with.
587        //
588        // The failed-submit report is reached by `dioring/tests/submits.rs`, through a responder
589        // registered for this ring, which fails the submits pool threads make as well (DI-7.1).
590        let popped = {
591            let mut engine = self.engine();
592            let submitted = engine.backend.submit();
593            match engine.core.pop() {
594                Some(entry) => Ok(Some(entry)),
595                None => match (engine.take_fault(), submitted) {
596                    (Some(fault), _) => Err(io::Error::other(fault)),
597                    (None, Err(cause)) => Err(io::Error::other(cause)),
598                    (None, Ok(())) => Ok(None),
599                },
600            }
601        };
602        // What a drain asked of the provider waits for a consumer call, and a pop is one (DI-D-44).
603        self.call_provider();
604        popped
605    }
606
607    fn make_durable_through(
608        &mut self,
609        through: Tag<'_, Self::Ids>,
610    ) -> Result<DurabilityRequest<Self::Ids>, SealError<Self::Ids>> {
611        let sealed = self.engine().make_durable_through(through.epoch());
612        let (answer, deliver) = sealed?;
613        self.settled(deliver);
614        Ok(answer)
615    }
616
617    fn durable_through(&self, lineage: Lineage) -> Result<Option<E>, UnknownLineage<Self::Ids>> {
618        self.engine().core.durable_through(lineage)
619    }
620
621    fn sealed_through(&self, lineage: Lineage) -> Result<Option<E>, UnknownLineage<Self::Ids>> {
622        self.engine().core.sealed_through(lineage)
623    }
624
625    fn epoch_state(
626        &self,
627        epoch: Epoch<Self::Ids>,
628    ) -> Result<EpochState<Self::Ids>, UnknownLineage<Self::Ids>> {
629        self.engine().core.epoch_state(epoch)
630    }
631
632    fn default_lineage(&self) -> LineageHandle {
633        self.default.clone()
634    }
635
636    fn mint_lineage(&mut self, description: Option<String>) -> LineageHandle {
637        let lineage = self.engine().core.mint_lineage(description);
638        LineageHandle::mint(lineage, home(&self.hub))
639    }
640
641    fn lineages(&self) -> Vec<LineageInfo<Self::Ids>> {
642        self.engine().core.lineages()
643    }
644
645    fn end_lineage(&mut self, handle: LineageHandle) -> Result<(), EndLineageError<Self::Ids>> {
646        let lineage = handle.lineage();
647        let reason = if !self.owns(&handle) {
648            EndLineageRefusal::Foreign(lineage)
649        } else if lineage == self.default.lineage() {
650            EndLineageRefusal::Default
651        } else {
652            match handle.take_last() {
653                Ok(lineage) => {
654                    let ended = {
655                        let mut engine = self.engine();
656                        let ended = engine.core.end_lineage(lineage);
657                        ended.map(|actions| engine.act(actions))
658                    };
659                    match ended {
660                        Ok(deliver) => self.settled(deliver),
661                        // Live, since this was its last handle and taking it disarmed it: a
662                        // refusal means a release ended it already.
663                        Err(refusal) => self.hub.inconsistent(
664                            "the last handle of a lineage found it already ended",
665                            format_args!("lineage {lineage:?}, refused as {refusal:?}"),
666                        ),
667                    }
668                    return Ok(());
669                }
670                Err(handle) => {
671                    return Err(EndLineageError {
672                        reason: EndLineageRefusal::Shared,
673                        handle,
674                    });
675                }
676            }
677        };
678        Err(EndLineageError { reason, handle })
679    }
680
681    fn retire_lineage(
682        &mut self,
683        handle: LineageHandle,
684    ) -> Result<(), RetireLineageError<Self::Ids>> {
685        let lineage = handle.lineage();
686        let reason = if !self.owns(&handle) {
687            RetireLineageRefusal::Foreign(lineage)
688        } else if lineage == self.default.lineage() {
689            RetireLineageRefusal::Default
690        } else if !handle.is_last() {
691            RetireLineageRefusal::Shared
692        } else {
693            // Unlocked before the handle is consumed: a handle's release takes this lock.
694            let retired = self.engine().core.retire_lineage(lineage);
695            match retired {
696                Err(refusal) => refusal,
697                Ok(()) => {
698                    // The last copy, owned here, so none can be made.
699                    let taken = handle.take_last();
700                    debug_assert!(taken.is_ok(), "the last handle, owned here");
701                    return Ok(());
702                }
703            }
704        };
705        Err(RetireLineageError { reason, handle })
706    }
707
708    fn resolve(
709        &mut self,
710        items: Vec<(FailureToken, Resolution)>,
711    ) -> Result<(), ResolveError<Self::Ids>> {
712        let resolved = {
713            let mut engine = self.engine();
714            engine
715                .core
716                .resolve(items)
717                .map(|actions| engine.act(actions))
718        };
719        let deliver = resolved?;
720        self.settled(deliver);
721        Ok(())
722    }
723
724    fn import_failure(
725        &mut self,
726        scope: ImportScope<Self::Ids>,
727    ) -> Result<Imported<Self::Ids>, UnknownLineage<Self::Ids>> {
728        let imported = {
729            let mut engine = self.engine();
730            engine
731                .core
732                .import_failure(scope)
733                .map(|(imported, actions)| (imported, engine.act(actions)))
734        };
735        let (imported, deliver) = imported?;
736        self.settled(deliver);
737        Ok(imported)
738    }
739
740    fn failures(&self) -> Vec<FailureInfo<Self::Ids>> {
741        self.engine().core.failures()
742    }
743
744    fn take_token(&mut self, failure: FailureId) -> Option<FailureToken> {
745        self.engine().core.take_token(failure)
746    }
747
748    fn close(mut self) -> Leftovers<Self::Ids, B, C> {
749        // `close` consumes the instance, so nothing has ended it before.
750        self.end().unwrap_or_else(|| Leftovers {
751            held: Vec::new(),
752            failures: Vec::new(),
753            lineages: Vec::new(),
754        })
755    }
756}
757
758impl<B, E, C, R, K> Dioring<B, E, C, R, K>
759where
760    B: IoBuf + Send + 'static,
761    E: EpochId + Send + Sync + 'static,
762    C: Send + 'static,
763    R: IoBufMut + Send + 'static,
764    K: TimeBase,
765{
766    /// Drain what the ring has completed, on this thread (DI-D-55): drain it to empty and, if that
767    /// delivered entries, return; otherwise wait up to `timeout` -- `None` is no limit, and
768    /// `Duration::ZERO` is a pump that does not wait -- and drain again. Then make the provider's
769    /// requests, and set the readiness event if entries became poppable. Whether they did. Pop what
770    /// became poppable with [`DurableRing::pop`].
771    ///
772    /// **Only for an instance drained by its consumer**, built with [`DeliveryMode::Pumped`] or
773    /// [`DeliveryMode::Signalled`]: a completion waits in the ring until a pump, so an instance that
774    /// is never pumped delivers nothing. A pumped instance waits on the ring's own completion
775    /// event, and a signalled one on its [`completion_signal`](Self::completion_signal), which the
776    /// consumer may be waiting on too. **It drains before it waits**, so a wait of the consumer's
777    /// own that took the signal cannot leave a pump blocked while completions sit in the ring. The
778    /// wait is made with the engine's lock released, so another thread's push, a provider's answer or
779    /// a lineage handle's release is not held up by it.
780    ///
781    /// A return does not promise entries: the wait is satisfied by a raising whose completions an
782    /// earlier drain took, and by the one signal the ring crate raises as it attaches the event
783    /// ([D-20](../../windows-ioring-sys/DESIGN-NOTES.md#d-20)), so a first pump may return at once.
784    /// Pump in a loop, and pop to empty each time. With no limit and nothing in flight a pump never
785    /// returns.
786    ///
787    /// # Errors
788    ///
789    /// [`io::ErrorKind::Unsupported`] for an instance built with [`DeliveryMode::Pool`], which the
790    /// pool drains. Otherwise the wait's own error, when the event cannot be waited on; the drain is
791    /// made all the same, so what has completed is delivered, and the entries it made poppable are
792    /// there to pop.
793    pub fn pump(&mut self, timeout: Option<Duration>) -> io::Result<bool> {
794        let Some(pumped) = self
795            .delivery
796            .as_mut()
797            .expect("an instance is live until it ends")
798            .pump(timeout)
799        else {
800            return Err(io::Error::new(
801                io::ErrorKind::Unsupported,
802                "the instance was built with pool delivery, which drains it; pumping is for \
803                 DeliveryMode::Pumped and DeliveryMode::Signalled",
804            ));
805        };
806        self.settled(pumped.ready);
807        pumped.waited.map(|_| pumped.ready)
808    }
809
810    /// A duplicate of the event the thread pool sets whenever completions may be waiting, for the
811    /// consumer to wait on beside its own handles, and then to [`pump`](Self::pump) (DI-7.5). Each
812    /// call makes another duplicate of the one event.
813    ///
814    /// The event is the instance's own and shares nothing with the ring: it is auto-reset, so a wait
815    /// takes the signal, and the consumer may set or reset it, which costs it nothing but its own
816    /// wakeup, since a pump drains before it waits. It is set once as the instance is built, so a
817    /// first wait may return with nothing to drain.
818    ///
819    /// # Errors
820    ///
821    /// [`io::ErrorKind::Unsupported`] unless the instance was built with [`DeliveryMode::Signalled`].
822    /// Otherwise the duplication's: the process cannot get another handle.
823    pub fn completion_signal(&self) -> io::Result<Event> {
824        self.delivery
825            .as_ref()
826            .expect("an instance is live until it ends")
827            .signal()
828            .unwrap_or_else(|| {
829                Err(io::Error::new(
830                    io::ErrorKind::Unsupported,
831                    "only an instance built with DeliveryMode::Signalled has a completion signal",
832                ))
833            })
834    }
835
836    /// A handle to the ring's own completion event, for the consumer to wait on beside its own
837    /// handles, and then to [`pump`](Self::pump), with no thread-pool callback in between
838    /// ([DI-D-55](../../durable-ioring/DESIGN-NOTES.md#di-d-55)). Each call makes another
839    /// duplicate of the one event.
840    ///
841    /// The handle carries `SYNCHRONIZE` access alone, so it can wait and nothing else: the kernel
842    /// refuses a set or a reset of the ring's event through it. A holder that duplicates it asking
843    /// for more access can still get it, which Windows allows, so this guards against a handle used
844    /// for what it was not handed out for and is not a boundary within the process.
845    ///
846    /// **It is the ring's event, not a copy of it.** The event is auto-reset, so a wait through this
847    /// handle takes the signal, and the pump's own wait does not see it; the pump drains before it
848    /// waits, so a consumer that waits here and then pumps on the same thread loses nothing. A
849    /// second thread blocked in [`pump`](Self::pump) with no limit while another waits here can be
850    /// left waiting for a signal the other took, so wait and pump from one thread. The ring signals
851    /// the event once as it is attached, so a first wait may return with nothing to drain.
852    ///
853    /// # Errors
854    ///
855    /// [`io::ErrorKind::Unsupported`] unless the instance was built with [`DeliveryMode::Pumped`]:
856    /// the other modes give the event to a wake of their own, which a wait of the consumer's would
857    /// take wakes from ([`completion_signal`](Self::completion_signal) is the event a signalled
858    /// instance offers). Otherwise the duplication's: the process cannot get another handle.
859    pub fn completion_event(&self) -> io::Result<OwnedHandle> {
860        self.delivery
861            .as_ref()
862            .expect("an instance is live until it ends")
863            .completion_event()
864            .unwrap_or_else(|| {
865                Err(io::Error::new(
866                    io::ErrorKind::Unsupported,
867                    "only an instance built with DeliveryMode::Pumped offers the ring's own \
868                     completion event; a signalled one offers completion_signal",
869                ))
870            })
871    }
872
873    /// A consumer's push whose doorbell failed: set the readiness event, once, so the consumer pops,
874    /// which rings it again and reports a cause that stays (DI-7+.2). With the engine's lock
875    /// released.
876    fn attend(&self, attention: bool) {
877        if attention {
878            self.hub.signal();
879        }
880    }
881
882    /// After a consumer call, with the engine's lock released: set the readiness event if the call
883    /// made entries poppable, then make the provider's requests.
884    fn settled(&mut self, deliver: bool) {
885        if deliver {
886            self.hub.signal();
887        }
888        self.call_provider();
889    }
890
891    /// Make every request for the consumer's provider made so far, in order, with the engine's lock
892    /// released: the provider may answer inside its call, and the answer takes the lock
893    /// (DI-D-44). Every request is built before the first is made, so a panic in the provider drops
894    /// the completions of those after it unanswered: each a failure of its domain, as DI-D-27 says.
895    fn call_provider(&mut self) {
896        let requests = self.engine().take_requests();
897        if requests.is_empty() {
898            return;
899        }
900        let home = answers(&self.hub);
901        let requests: Vec<FlushRequest<WindowsIds<E>>> = requests
902            .into_iter()
903            .map(|request| flush_request(request, &home))
904            .collect();
905        let provider = self
906            .provider
907            .as_mut()
908            .expect("only a provider's domains make requests");
909        for request in requests {
910            provider.make_durable(request);
911        }
912    }
913}
914
915impl<B: IoBuf, E: EpochId + 'static, C, R: IoBufMut, K: StampClock> Dioring<B, E, C, R, K> {
916    /// The engine, locked: what a consumer call acts through.
917    pub(crate) fn engine(&self) -> EngineGuard<'_, PoolEngine<E, B, C, K, R>> {
918        self.delivery
919            .as_ref()
920            .expect("an instance is live until it ends")
921            .engine()
922    }
923
924    /// Whether `handle` is one this instance minted. The lineage a handle names and its instance
925    /// are fields anyone can fill in, through [`LineageHandle::detached`], so a fabricated handle
926    /// for a real lineage would hold a reference count of its own and let an end or a retire go
927    /// ahead while the genuine handles are live; the home a handle reports to is the instance's
928    /// own, and is the proof.
929    fn owns(&self, handle: &LineageHandle) -> bool {
930        handle.is_homed_at(&self.hub)
931    }
932
933    /// End the instance, as the module's "Ending" says, returning what is left; `None` if it has
934    /// ended already.
935    pub(crate) fn end(&mut self) -> Option<Leftovers<DioringIds<E, K::Instant>, B, C>> {
936        let delivery = self.delivery.take()?;
937        // Stops the wake, waiting for a drain in progress, and takes the engine out of its slot.
938        let engine = delivery.into_engine();
939        // Before the wait: a completion the provider keeps in itself is answered, as abandoned, by
940        // the provider's drop, which would otherwise wait for the wait it holds up.
941        self.provider = None;
942        self.hub.wait_for_answers();
943        let Engine { core, backend, .. } = engine;
944        let leftovers = core.close();
945        // The ring runs down every operation still in flight as it goes.
946        drop(backend);
947        Some(leftovers)
948    }
949}
950
951/// A request in the core's terms, as the provider is handed it: each domain's part, with the
952/// completion that answers it through `home`.
953fn flush_request<E: EpochId + 'static>(
954    request: ProviderRequest<WindowsIds<E>>,
955    home: &Weak<dyn Answer>,
956) -> FlushRequest<WindowsIds<E>> {
957    let ProviderRequest {
958        lineage,
959        seal,
960        domains,
961    } = request;
962    let domains = domains
963        .into_iter()
964        .map(|ProviderPart { domain, targets }| DomainRequest {
965            completion: DomainCompletion::new(
966                Weak::clone(home),
967                DomainCall {
968                    lineage,
969                    seal,
970                    name: domain.clone(),
971                },
972            ),
973            domain,
974            files: targets,
975        })
976        .collect();
977    FlushRequest { domains }
978}
979
980/// A span of the registered buffers, as the backend names it.
981fn region(span: RegisteredSpan) -> RegisteredRegion {
982    RegisteredRegion {
983        index: span.buffer_index,
984        offset: span.offset,
985        length: span.len,
986    }
987}
988
989/// The fault seam (`DI-3.2.4`): failures on demand, for tests and for the contract's worked
990/// examples. It transforms a commit's real completion through `windows-ioring-sys`' own seam, so
991/// the commit still reaches the kernel and only the answer dioring sees changes -- the backend's
992/// `fail_next_commit`, under the contract's older word for a commit (DI-11.2).
993#[cfg(feature = "fault-injection")]
994impl<B: IoBuf, E: EpochId + 'static, C, R: IoBufMut, K: StampClock> Dioring<B, E, C, R, K> {
995    /// Make the next flush of `file` to complete report the Win32 error `code` -- for example
996    /// `1117`, `ERROR_IO_DEVICE` -- whatever the kernel answered. Armed failures for one file are
997    /// used in the order they were armed. The flush may already be in flight.
998    pub fn fail_next_flush(&mut self, file: IoTargetKey, code: u32) {
999        self.engine().backend.fail_next_commit(file, code);
1000    }
1001}
1002
1003impl<B, E, C, R, K> RegisteredBufferRing for Dioring<B, E, C, R, K>
1004where
1005    B: IoBuf + Send + 'static,
1006    E: EpochId + Send + Sync + 'static,
1007    C: Send + 'static,
1008    R: IoBufMut + Send + 'static,
1009    K: TimeBase,
1010{
1011    type Registered = R;
1012
1013    fn with_registered_buffer<T>(&mut self, i: u32, f: impl FnOnce(&[u8]) -> T) -> io::Result<T> {
1014        self.engine().backend.registered_buffer(i).map(f)
1015    }
1016
1017    fn with_registered_buffer_mut<T>(
1018        &mut self,
1019        i: u32,
1020        f: impl FnOnce(&mut [u8]) -> T,
1021    ) -> io::Result<T> {
1022        self.engine().backend.registered_buffer_mut(i).map(f)
1023    }
1024
1025    fn write_registered_with(
1026        &mut self,
1027        file: IoTargetKey,
1028        offset: u64,
1029        span: RegisteredSpan,
1030        tag: Tag<'_, Self::Ids>,
1031        context: C,
1032        options: WriteOptions<Self::Ids>,
1033    ) -> PushResult<Self> {
1034        self.engine().write(
1035            file,
1036            offset,
1037            SubmissionData::Registered(region(span)),
1038            tag.epoch(),
1039            context,
1040            options,
1041        )
1042    }
1043
1044    fn read_registered_with(
1045        &mut self,
1046        file: IoTargetKey,
1047        offset: u64,
1048        span: RegisteredSpan,
1049        context: C,
1050        options: ReadOptions<Self::Ids>,
1051    ) -> PushResult<Self> {
1052        self.engine().read(
1053            file,
1054            offset,
1055            SubmissionData::Registered(region(span)),
1056            context,
1057            options,
1058        )
1059    }
1060}
1061
1062impl<B: IoBuf, E: EpochId + 'static, C, R: IoBufMut, K: StampClock> Drop
1063    for Dioring<B, E, C, R, K>
1064{
1065    fn drop(&mut self) {
1066        // Ends the instance on this thread, never inside one of delivery's own calls, where
1067        // stopping delivery would wait for itself. Nothing to do after `close()`, which has ended
1068        // it already; drop discards what it would have returned.
1069        let _ = self.end();
1070    }
1071}
1072/// Register `files` with `ring`, in order, and wait for the registration to complete. `None` for
1073/// no files: an instance whose files are all added later makes no file registration.
1074///
1075/// Waits `bound` for the completion. Each way it fails ends construction as
1076/// `SetupRefusal::Ring`, and each is reached by a test (DI-7.2) through the ring crate's safe
1077/// responder, registered on the ring as it is created: a build refused, a submit failed, a
1078/// completion failed, and a completion that does not arrive within `bound`. Tried on the
1079/// development machine, the kernel completed a registration of a thread handle, and of a
1080/// zero-length buffer, successfully, so no real input reaches them; a ring without the registration
1081/// op (an older Windows) takes the refused build's path, and more than `u32::MAX` files takes it
1082/// too, refused before submission.
1083pub(crate) fn register_files<B, S>(
1084    ring: &mut IoRing<B, S>,
1085    handles: Vec<SharedHandle>,
1086    bound: Duration,
1087) -> io::Result<Option<RegisteredFiles>> {
1088    if handles.is_empty() {
1089        return Ok(None);
1090    }
1091    // The caller's clones: the ring keeps them for its life, and the caller keeps its own.
1092    let mut batch = Batch::new(ring);
1093    let pending = batch
1094        .register_shared_files(handles)
1095        .map_err(PushRefused::into_error)?;
1096    batch.submit()?;
1097    let completion = registration_completion(ring, bound)?;
1098    match pending.claim_if(&completion) {
1099        Ok(result) => result.map(Some),
1100        // Unreachable in practice: nothing else is in flight on a ring this function created a
1101        // moment ago, so the one completion is the registration's.
1102        Err(_) => Err(io::Error::other(
1103            "the registration's completion named another operation",
1104        )),
1105    }
1106}
1107
1108/// A buffer registration's outcome: the registration, or `None` for no buffers; or the error with
1109/// the buffers, while the kernel has not been given them, and `None` once it has.
1110pub(crate) type BufferRegistration<R> =
1111    Result<Option<RegisteredBuffers<R>>, (io::Error, Option<Vec<R>>)>;
1112
1113/// Register `buffers` with `ring`, and wait `bound` for the registration to complete. Its error
1114/// paths are reached as [`register_files`]' are; a refused build hands the buffers back, and a
1115/// failure after the kernel was given them does not.
1116pub(crate) fn register_buffers<B, S, R: IoBufMut>(
1117    ring: &mut IoRing<B, S>,
1118    buffers: Vec<R>,
1119    bound: Duration,
1120) -> BufferRegistration<R> {
1121    if buffers.is_empty() {
1122        return Ok(None);
1123    }
1124    let mut batch = Batch::new(ring);
1125    let pending = batch
1126        .register_buffers(buffers)
1127        .map_err(|refused| (refused.error, refused.payload))?;
1128    batch.submit().map_err(|error| (error, None))?;
1129    let completion = registration_completion(ring, bound).map_err(|error| (error, None))?;
1130    match pending.claim_if(&completion) {
1131        Ok(result) => result.map(Some).map_err(|error| (error, None)),
1132        // Unreachable in practice, as for files.
1133        Err(_) => Err((
1134            io::Error::other("the registration's completion named another operation"),
1135            None,
1136        )),
1137    }
1138}
1139
1140/// The completion of the one registration in flight on `ring`, within `bound`.
1141fn registration_completion<B, S>(
1142    ring: &mut IoRing<B, S>,
1143    bound: Duration,
1144) -> io::Result<Completion> {
1145    match ring.pop_within(bound)? {
1146        Some((completion, _)) => Ok(completion),
1147        None => Err(io::Error::new(
1148            io::ErrorKind::TimedOut,
1149            format!("the ring did not complete a registration within {bound:?}"),
1150        )),
1151    }
1152}