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}