win_durable_ioring/delivery.rs
1// Copyright (c) 2026 Mike Grier
2//! The Model A front end ([DI-D-31](../DESIGN-NOTES.md#di-d-31),
3//! [DI-D-32](../DESIGN-NOTES.md#di-d-32)): an instance whose entries are handed, one at a time and
4//! in queue order, to a handler on thread-pool threads, and whose operations are reachable from any
5//! thread through `&self`.
6//!
7//! Written once over the trait, so it serves every implementation whose readiness signal is an
8//! `Event` (DI-D-52 Q6). It waits on that signal through a thread-pool wait, and on each wake pops
9//! to `None` before waiting again (DI-D-28). The handler is called with the instance's lock
10//! released, so it may call back in through the [`DeliveryHandle`] it is given; it runs under a
11//! lock of its own, which only the single deliverer takes.
12
13use std::fmt;
14use std::io;
15use std::ops::Deref;
16use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
17
18use win_sync_sys::Event;
19use windows_ioring_sys::{IoBuf, IoBufMut, RegisteredSpan};
20use windows_threadpool_sys::callback_env::CallbackEnviron;
21use windows_threadpool_sys::wait::{ThreadpoolWait, WaitActivation};
22
23use durable_ioring::{
24 AddTargetError, DurabilityRequest, EndLineageError, Epoch, EpochState, FailureInfo,
25 ImportScope, Imported, IoTargetKey, Leftovers, LineageInfo, ReadOptions, RemoveTargetError,
26 Resolution, ResolveError, RetireLineageError, SealError, Tag, UnknownLineage, WriteOptions,
27};
28use durable_ioring::{
29 DurableRing, EntryOf, EpochIdOf, FailureIdOf, HandleOf, Lin, PushResult, RegisteredBufferRing,
30 TokenOf,
31};
32use durable_ioring::{ReadKey, TargetKey, WriteKey};
33
34#[cfg(test)]
35mod tests;
36
37/// What the deliverer hands its handler: an entry, or why it could not pop one.
38///
39/// A failure is the instance's own pop failing -- a submission its doorbell could not issue, or a
40/// ring it could not read ([DI-D-58](../../durable-ioring/DESIGN-NOTES.md#di-d-58)). It is reported
41/// once per wake, and the delivery goes on: the next wake delivers what can be, and a cause that
42/// stays is reported again. The handler's remedy for one that stays is to stop and drop the
43/// delivery, which hands the instance back (into_inner) or closes it (close).
44#[derive(Debug)]
45pub enum DeliveryEvent<E> {
46 /// The next entry, in queue order.
47 Entry(E),
48 /// The instance's `pop` failed. The error says why, and downcasts to the cause the instance
49 /// reported, as an instance's own `pop` error does.
50 Failure(io::Error),
51}
52
53/// The handler, as the deliverer holds it.
54type Handler<D> = dyn FnMut(DeliveryEvent<EntryOf<D>>, &DeliveryHandle<D>) + Send;
55
56/// What the deliverer and the owner share. The handler is last, so a front end built with one
57/// handler type can be held as the trait object.
58struct Inner<D, H: ?Sized> {
59 handle: DeliveryHandle<D>,
60 handler: Mutex<H>,
61}
62
63/// The operations of an instance whose delivery an [`EntryDelivery`] owns, through `&self` from
64/// any thread. The handler is given one; the owner derefs to its own, so both offer the same
65/// operations from one definition.
66///
67/// Each call takes the front end's lock on the instance for its duration -- dioring's lock in
68/// DI-D-18's order, taken before the ring's -- and the handler is called with that lock released.
69///
70/// # What it withholds
71///
72/// - **`pop` and `readiness`**: the delivery is the single deliverer and the signal's one waiter
73/// (DI-D-28). A second popper would take entries out of order; a second waiter would steal wakes.
74/// - **Any `&mut` to the instance**, including through a closure. Safe code could then replace the
75/// instance while the wait stayed armed on the old one's signal, and delivery would stop without
76/// an error -- the ring crate's measured defect
77/// ([D-43](../../windows-ioring-sys/DESIGN-NOTES.md#d-43)), which a closure does not close.
78/// Registered bytes are therefore reached through closures over the bytes alone.
79///
80/// Not constructible, and not `Clone`: a handler holds one only for the length of a call, so it
81/// cannot keep the front end alive.
82pub struct DeliveryHandle<D> {
83 /// `None` only after [`EntryDelivery::into_inner`] has quiesced the delivery and taken it.
84 ring: Mutex<Option<D>>,
85}
86
87impl<D> DeliveryHandle<D> {
88 /// Run `f` on the instance under the front end's lock.
89 fn with<R>(&self, f: impl FnOnce(&mut D) -> R) -> R {
90 let mut ring = self.ring.lock().unwrap_or_else(PoisonError::into_inner);
91 f(ring
92 .as_mut()
93 .expect("the instance is taken only once nothing can reach this handle"))
94 }
95
96 fn take(&self) -> D {
97 self.ring
98 .lock()
99 .unwrap_or_else(PoisonError::into_inner)
100 .take()
101 .expect("the instance is taken once")
102 }
103}
104
105impl<D: DurableRing> DeliveryHandle<D> {
106 /// [`DurableRing::add_target`].
107 ///
108 /// # Errors
109 ///
110 /// As [`DurableRing::add_target`].
111 pub fn add_target<K: TargetKey>(
112 &self,
113 key: K,
114 target: D::Target,
115 ) -> Result<(), AddTargetError<D::Target>> {
116 self.with(|ring| ring.add_target(key, target))
117 }
118
119 /// [`DurableRing::remove_target`].
120 ///
121 /// # Errors
122 ///
123 /// As [`DurableRing::remove_target`].
124 pub fn remove_target(&self, key: IoTargetKey) -> Result<D::Target, RemoveTargetError<D::Ids>> {
125 self.with(|ring| ring.remove_target(key))
126 }
127
128 /// [`DurableRing::default_lineage`].
129 pub fn default_lineage(&self) -> HandleOf<D> {
130 self.with(|ring| ring.default_lineage())
131 }
132
133 /// [`DurableRing::mint_lineage`].
134 pub fn mint_lineage(&self, description: Option<String>) -> HandleOf<D> {
135 self.with(|ring| ring.mint_lineage(description))
136 }
137
138 /// [`DurableRing::lineages`].
139 pub fn lineages(&self) -> Vec<LineageInfo<D::Ids>> {
140 self.with(|ring| ring.lineages())
141 }
142
143 /// [`DurableRing::end_lineage`].
144 ///
145 /// # Errors
146 ///
147 /// As [`DurableRing::end_lineage`].
148 pub fn end_lineage(&self, handle: HandleOf<D>) -> Result<(), EndLineageError<D::Ids>> {
149 self.with(|ring| ring.end_lineage(handle))
150 }
151
152 /// [`DurableRing::retire_lineage`].
153 ///
154 /// # Errors
155 ///
156 /// As [`DurableRing::retire_lineage`].
157 pub fn retire_lineage(&self, handle: HandleOf<D>) -> Result<(), RetireLineageError<D::Ids>> {
158 self.with(|ring| ring.retire_lineage(handle))
159 }
160
161 /// [`DurableRing::write`].
162 ///
163 /// # Errors
164 ///
165 /// As [`DurableRing::write`].
166 pub fn write<K: WriteKey>(
167 &self,
168 file: K,
169 offset: u64,
170 buffer: D::Buffer,
171 tag: Tag<'_, D::Ids>,
172 context: D::Context,
173 ) -> PushResult<D>
174 where
175 D::Buffer: IoBuf,
176 {
177 self.with(|ring| ring.write(file, offset, buffer, tag, context))
178 }
179
180 /// [`DurableRing::write_with`].
181 ///
182 /// # Errors
183 ///
184 /// As [`DurableRing::write_with`].
185 pub fn write_with<K: WriteKey>(
186 &self,
187 file: K,
188 offset: u64,
189 buffer: D::Buffer,
190 tag: Tag<'_, D::Ids>,
191 context: D::Context,
192 options: WriteOptions<D::Ids>,
193 ) -> PushResult<D>
194 where
195 D::Buffer: IoBuf,
196 {
197 self.with(|ring| ring.write_with(file, offset, buffer, tag, context, options))
198 }
199
200 /// [`DurableRing::read`].
201 ///
202 /// # Errors
203 ///
204 /// As [`DurableRing::read`].
205 pub fn read<K: ReadKey>(
206 &self,
207 file: K,
208 offset: u64,
209 buffer: D::Buffer,
210 context: D::Context,
211 ) -> PushResult<D>
212 where
213 D::Buffer: IoBufMut,
214 {
215 self.with(|ring| ring.read(file, offset, buffer, context))
216 }
217
218 /// [`DurableRing::read_with`].
219 ///
220 /// # Errors
221 ///
222 /// As [`DurableRing::read_with`].
223 pub fn read_with<K: ReadKey>(
224 &self,
225 file: K,
226 offset: u64,
227 buffer: D::Buffer,
228 context: D::Context,
229 options: ReadOptions<D::Ids>,
230 ) -> PushResult<D>
231 where
232 D::Buffer: IoBufMut,
233 {
234 self.with(|ring| ring.read_with(file, offset, buffer, context, options))
235 }
236
237 /// [`DurableRing::make_durable_through`].
238 ///
239 /// # Errors
240 ///
241 /// As [`DurableRing::make_durable_through`].
242 pub fn make_durable_through(
243 &self,
244 through: Tag<'_, D::Ids>,
245 ) -> Result<DurabilityRequest<D::Ids>, SealError<D::Ids>> {
246 self.with(|ring| ring.make_durable_through(through))
247 }
248
249 /// [`DurableRing::durable_through`].
250 ///
251 /// # Errors
252 ///
253 /// As [`DurableRing::durable_through`].
254 pub fn durable_through(
255 &self,
256 lineage: Lin<D>,
257 ) -> Result<Option<EpochIdOf<D>>, UnknownLineage<D::Ids>> {
258 self.with(|ring| ring.durable_through(lineage))
259 }
260
261 /// [`DurableRing::sealed_through`].
262 ///
263 /// # Errors
264 ///
265 /// As [`DurableRing::sealed_through`].
266 pub fn sealed_through(
267 &self,
268 lineage: Lin<D>,
269 ) -> Result<Option<EpochIdOf<D>>, UnknownLineage<D::Ids>> {
270 self.with(|ring| ring.sealed_through(lineage))
271 }
272
273 /// [`DurableRing::epoch_state`].
274 ///
275 /// # Errors
276 ///
277 /// As [`DurableRing::epoch_state`].
278 pub fn epoch_state(
279 &self,
280 epoch: Epoch<D::Ids>,
281 ) -> Result<EpochState<D::Ids>, UnknownLineage<D::Ids>> {
282 self.with(|ring| ring.epoch_state(epoch))
283 }
284
285 /// [`DurableRing::resolve`].
286 ///
287 /// # Errors
288 ///
289 /// As [`DurableRing::resolve`].
290 pub fn resolve(
291 &self,
292 items: Vec<(TokenOf<D>, Resolution)>,
293 ) -> Result<(), ResolveError<D::Ids>> {
294 self.with(|ring| ring.resolve(items))
295 }
296
297 /// [`DurableRing::import_failure`].
298 ///
299 /// # Errors
300 ///
301 /// As [`DurableRing::import_failure`].
302 pub fn import_failure(
303 &self,
304 scope: ImportScope<D::Ids>,
305 ) -> Result<Imported<D::Ids>, UnknownLineage<D::Ids>> {
306 self.with(|ring| ring.import_failure(scope))
307 }
308
309 /// [`DurableRing::failures`].
310 pub fn failures(&self) -> Vec<FailureInfo<D::Ids>> {
311 self.with(|ring| ring.failures())
312 }
313
314 /// [`DurableRing::take_token`].
315 pub fn take_token(&self, failure: FailureIdOf<D>) -> Option<TokenOf<D>> {
316 self.with(|ring| ring.take_token(failure))
317 }
318}
319
320impl<D: RegisteredBufferRing> DeliveryHandle<D> {
321 /// Run `f` on the bytes of registered buffer `i`, under the front end's lock.
322 ///
323 /// # Errors
324 ///
325 /// As [`RegisteredBufferRing::with_registered_buffer`].
326 pub fn with_registered_buffer<R>(&self, i: u32, f: impl FnOnce(&[u8]) -> R) -> io::Result<R> {
327 self.with(|ring| ring.with_registered_buffer(i, f))
328 }
329
330 /// Run `f` on the bytes of registered buffer `i`, mutably, under the front end's lock.
331 ///
332 /// # Errors
333 ///
334 /// As [`RegisteredBufferRing::with_registered_buffer_mut`].
335 pub fn with_registered_buffer_mut<R>(
336 &self,
337 i: u32,
338 f: impl FnOnce(&mut [u8]) -> R,
339 ) -> io::Result<R> {
340 self.with(|ring| ring.with_registered_buffer_mut(i, f))
341 }
342
343 /// [`RegisteredBufferRing::write_registered`].
344 ///
345 /// # Errors
346 ///
347 /// As [`RegisteredBufferRing::write_registered`].
348 pub fn write_registered<K: WriteKey>(
349 &self,
350 file: K,
351 offset: u64,
352 span: RegisteredSpan,
353 tag: Tag<'_, D::Ids>,
354 context: D::Context,
355 ) -> PushResult<D> {
356 self.with(|ring| ring.write_registered(file, offset, span, tag, context))
357 }
358
359 /// [`RegisteredBufferRing::write_registered_with`].
360 ///
361 /// # Errors
362 ///
363 /// As [`RegisteredBufferRing::write_registered_with`].
364 pub fn write_registered_with<K: WriteKey>(
365 &self,
366 file: K,
367 offset: u64,
368 span: RegisteredSpan,
369 tag: Tag<'_, D::Ids>,
370 context: D::Context,
371 options: WriteOptions<D::Ids>,
372 ) -> PushResult<D> {
373 self.with(|ring| ring.write_registered_with(file, offset, span, tag, context, options))
374 }
375
376 /// [`RegisteredBufferRing::read_registered`].
377 ///
378 /// # Errors
379 ///
380 /// As [`RegisteredBufferRing::read_registered`].
381 pub fn read_registered<K: ReadKey>(
382 &self,
383 file: K,
384 offset: u64,
385 span: RegisteredSpan,
386 context: D::Context,
387 ) -> PushResult<D> {
388 self.with(|ring| ring.read_registered(file, offset, span, context))
389 }
390
391 /// [`RegisteredBufferRing::read_registered_with`].
392 ///
393 /// # Errors
394 ///
395 /// As [`RegisteredBufferRing::read_registered_with`].
396 pub fn read_registered_with<K: ReadKey>(
397 &self,
398 file: K,
399 offset: u64,
400 span: RegisteredSpan,
401 context: D::Context,
402 options: ReadOptions<D::Ids>,
403 ) -> PushResult<D> {
404 self.with(|ring| ring.read_registered_with(file, offset, span, context, options))
405 }
406}
407
408/// A failed [`EntryDelivery::new`], handing back what it took.
409pub struct DeliverySetupError<D, F> {
410 /// Why it failed.
411 pub error: io::Error,
412 /// The instance.
413 pub ring: D,
414 /// The handler.
415 pub on_entry: F,
416}
417
418impl<D, F> fmt::Debug for DeliverySetupError<D, F> {
419 // By hand, so neither the instance nor the handler need be `Debug`.
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 f.debug_struct("DeliverySetupError")
422 .field("error", &self.error)
423 .finish_non_exhaustive()
424 }
425}
426
427/// The Model A front end: owns an instance and hands its entries, one at a time and in queue
428/// order, to a handler on thread-pool threads. Derefs to the [`DeliveryHandle`] through which the
429/// instance's operations are reached. For an instance whose readiness signal is an [`Event`],
430/// which it waits on through the thread pool.
431///
432/// # The handler
433///
434/// Called with a [`DeliveryEvent`]: each entry, in queue order, and a failure of the instance's
435/// `pop` when it has one to report, which a handler that ignores is a delivery that stalls
436/// without saying so. Called on a pool thread, never twice at once, with the instance's lock released. It must not
437/// panic -- a panic unwinds into the thread pool's callback and aborts the process -- and while it
438/// runs no further entry is delivered, so a handler that blocks holds up delivery, and holds up
439/// [`into_inner`](Self::into_inner) and drop, which wait for it.
440///
441/// # Ending
442///
443/// Drop, [`close`](Self::close) and [`into_inner`](Self::into_inner) all quiesce the delivery
444/// first: no handler call is running or will start (DI-2.7 point 5), so none runs once they have
445/// returned. Drop then drops the instance, and `close` closes it, each discarding any entry not yet
446/// delivered; `into_inner` hands the instance back with those entries still queued, so it can be
447/// popped directly or closed.
448pub struct EntryDelivery<D: DurableRing<Readiness = Event> + Send + 'static> {
449 /// `None` once the delivery has been quiesced.
450 wait: Option<ThreadpoolWait>,
451 inner: Arc<Inner<D, Handler<D>>>,
452}
453
454impl<D: DurableRing<Readiness = Event> + Send + 'static> EntryDelivery<D> {
455 /// Take `ring` and deliver its entries to `on_entry`, on the default thread pool, or on the
456 /// pool and priority `env` selects.
457 ///
458 /// Entries already queued when the ring is handed over are delivered: the readiness signal may
459 /// already have been consumed, so this arms the wait and then sets the signal itself.
460 ///
461 /// # Errors
462 ///
463 /// A [`DeliverySetupError`] handing back the instance and the handler, when taking the
464 /// readiness signal from the instance, creating the thread-pool wait, or setting the signal
465 /// fails. Only the first is reached by a test: the others fail when the thread pool cannot
466 /// create a wait.
467 pub fn new<F>(
468 mut ring: D,
469 on_entry: F,
470 env: Option<&mut CallbackEnviron<'_>>,
471 ) -> Result<Self, DeliverySetupError<D, F>>
472 where
473 F: FnMut(DeliveryEvent<EntryOf<D>>, &DeliveryHandle<D>) + Send + 'static,
474 {
475 let clones = ring.readiness().map(|signal| (signal.clone(), signal));
476 let (signal, kick) = match clones {
477 Ok(clones) => clones,
478 Err(error) => {
479 return Err(DeliverySetupError {
480 error,
481 ring,
482 on_entry,
483 });
484 }
485 };
486 let inner: Arc<Inner<D, F>> = Arc::new(Inner {
487 handle: DeliveryHandle {
488 ring: Mutex::new(Some(ring)),
489 },
490 handler: Mutex::new(on_entry),
491 });
492
493 let deliverer: Arc<Inner<D, Handler<D>>> = inner.clone();
494 let wait = match ThreadpoolWait::new(
495 signal.into(),
496 move |activation| deliver(&deliverer, activation),
497 env,
498 ) {
499 Ok(wait) => wait,
500 // The refused wait dropped its callback, and the deliverer's reference with it.
501 Err(error) => return Err(recover(inner, error)),
502 };
503 wait.arm(None);
504 if let Err(error) = kick.set() {
505 wait.stop_and_drain();
506 drop(wait);
507 return Err(recover(inner, error));
508 }
509 Ok(Self {
510 wait: Some(wait),
511 inner,
512 })
513 }
514
515 /// Quiesce the delivery and hand back the instance, with any entry not yet delivered still in
516 /// its queue.
517 #[must_use]
518 pub fn into_inner(mut self) -> D {
519 self.quiesce();
520 self.inner.handle.take()
521 }
522
523 /// Quiesce the delivery and end the instance with [`DurableRing::close`], handing back what is
524 /// left. Entries not yet delivered are discarded with it.
525 pub fn close(self) -> Leftovers<D::Ids, D::Buffer, D::Context> {
526 self.into_inner().close()
527 }
528
529 /// Disarm the wait, wait for a delivery in progress to finish, and release the wait and the
530 /// deliverer's reference to the instance.
531 fn quiesce(&mut self) {
532 if let Some(wait) = self.wait.take() {
533 wait.stop_and_drain();
534 }
535 }
536}
537
538impl<D: DurableRing<Readiness = Event> + Send + 'static> Deref for EntryDelivery<D> {
539 type Target = DeliveryHandle<D>;
540
541 fn deref(&self) -> &DeliveryHandle<D> {
542 &self.inner.handle
543 }
544}
545
546impl<D: DurableRing<Readiness = Event> + Send + 'static> Drop for EntryDelivery<D> {
547 fn drop(&mut self) {
548 // Before the instance is released, which the field drop that follows does (DI-2.7 point
549 // 5): no handler call can still be reaching it.
550 self.quiesce();
551 }
552}
553
554/// One wake: pop to `None`, handing each entry to the handler with the instance's lock released,
555/// then re-arm (DI-D-28). Re-arming last is what serialises delivery: a wake that arrives while
556/// this one drains finds the signal set once it is re-armed, and starts only after this one has
557/// made its last handler call.
558///
559/// A failed `pop` is the handler's to be told, once, and ends the wake: dioring's is a submission
560/// its doorbell could not issue, or a ring it could not read, with nothing to pop, and the readiness
561/// edge this wake took is the only thing that told the consumer (DI-D-58). Looping again would
562/// repeat a cause that stays, with nothing to stop it.
563fn deliver<D: DurableRing>(inner: &Inner<D, Handler<D>>, activation: &WaitActivation<'_>) {
564 loop {
565 let event = match inner.handle.with(|ring| ring.pop()) {
566 Ok(Some(entry)) => DeliveryEvent::Entry(entry),
567 Ok(None) => break,
568 Err(error) => {
569 let mut handler = lock(&inner.handler);
570 (*handler)(DeliveryEvent::Failure(error), &inner.handle);
571 break;
572 }
573 };
574 let mut handler = lock(&inner.handler);
575 (*handler)(event, &inner.handle);
576 }
577 activation.rearm(None);
578}
579
580fn lock<T: ?Sized>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
581 // The handler aborts the process rather than unwinding, so a poisoned handler lock means a
582 // panic elsewhere, which leaves the handler as it was.
583 mutex.lock().unwrap_or_else(PoisonError::into_inner)
584}
585
586/// Take back the instance and the handler from a front end that never started delivering.
587fn recover<D, F>(inner: Arc<Inner<D, F>>, error: io::Error) -> DeliverySetupError<D, F> {
588 let Inner { handle, handler } = Arc::into_inner(inner)
589 .expect("no wait holds the deliverer's reference once construction has failed");
590 DeliverySetupError {
591 error,
592 ring: handle
593 .ring
594 .into_inner()
595 .unwrap_or_else(PoisonError::into_inner)
596 .expect("the instance is still in place"),
597 on_entry: handler.into_inner().unwrap_or_else(PoisonError::into_inner),
598 }
599}