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