Skip to main content

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}