dope-uring 0.38.0

Owned io_uring backend for dope
Documentation
use dope_sys::{
    io::{Close as _, Retry as _},
    kernel, memory,
    memory::{
        Failure as _,
        buffer::{provided, provided::Cleanup as _},
    },
    probe::{cleanup, cleanup::Cleanup as _},
    ring::{create, create::Build as _, map, map::Unmap as _, register::Registration as _},
    table::token,
};

use crate::{
    BufferBuild, Fault, Primary, Ring, error,
    reactor::{
        buffers::{self, Canary, Cleanup, Producer},
        register, ring,
        ring::Recover,
    },
};

pub struct Resources<T: token::Codec, L: memory::raw::Canary, P>(State<T, L, P>);

pub struct Build<T: token::Codec, L: memory::raw::Canary, P>(Result<P, Pending<T, L, P>>);

enum State<T: token::Codec, L: memory::raw::Canary, P> {
    Ring(ring::Cleanup),
    Probe(Recover<T, L, P>),
    Unbound(Pending<T, L, P>),
    Bound {
        pending: Pending<T, L, P>,
        context: buffers::raw::Context<L>,
    },
}

enum Pending<T: token::Codec, L: memory::raw::Canary, P> {
    Buffers(BufferBuild<L, P>),
    Open(Open<T, L, P>),
    Raw(Closed<T, P>),
    Provided(Provided<T, L, P>),
    Registration(Registration<T, L>),
}

struct Open<T, L: memory::raw::Canary, P> {
    ring: Ring<T>,
    buffers: BufferBuild<L, P>,
}

struct Closed<T, P> {
    primary: P,
    current: ring::Closing<T>,
}

struct Provided<T, L: memory::raw::Canary, P> {
    primary: P,
    current: register::raw::Retained<T>,
    buffers: Producer<L>,
}

struct Registration<T, L: memory::raw::Canary> {
    current: register::Cleanup<T>,
    buffers: Producer<L>,
}

impl<T: token::Codec, L: memory::raw::Canary, P> From<ring::Cleanup> for Resources<T, L, P> {
    fn from(state: ring::Cleanup) -> Self {
        Self(State::Ring(state))
    }
}

impl<T: token::Codec, L: memory::raw::Canary, P: Copy + From<Primary> + From<memory::Error>>
    create::Retain<P> for Resources<T, L, P>
{
    type Build = Build<T, L, P>;
    type Ring = Ring<T>;
    type Probe = Recover<T, L, P>;
    type Canary = Canary<L>;
    type Context = buffers::raw::Context<L>;
    type Buffers = Cleanup<L>;
    type Producer = Producer<L>;
    type Registration = register::Attempt<T>;
    type Registered = register::raw::Registered<T>;
    type BufferError = kernel::Errno<error::Register<22>>;

    fn probe(state: Recover<T, L, P>) -> Self {
        Self(State::Probe(state))
    }

    fn open<F: provided::Failure<Cleanup = Cleanup<L>>>(ring: Ring<T>, fault: F) -> Self::Build {
        let (error, cleanup) = fault.into_parts();
        let primary = P::from(error);
        match cleanup {
            Some(cleanup) => Build(Err(Pending::Open(Open {
                ring,
                buffers: BufferBuild::new(cleanup, primary),
            }))),
            None => Pending::close_raw(ring, primary),
        }
    }

    fn canary(ring: Ring<T>, primary: P, buffers: Canary<L>) -> Result<P, Self> {
        let cleanup = match buffers.try_close() {
            Ok(()) => return Pending::close_raw(ring, primary).into_result(),
            Err(cleanup) => cleanup,
        };
        Err(Self::unmap(ring, primary, cleanup))
    }

    fn unmap(ring: Ring<T>, primary: P, cleanup: Cleanup<L>) -> Self {
        Self(State::Unbound(Pending::Open(Open {
            ring,
            buffers: BufferBuild::new(cleanup, primary),
        })))
    }

    fn registration(state: register::Attempt<T>, buffers: Producer<L>) -> Self::Build {
        let result = match state.try_close() {
            Ok(error) => Pending::<T, L, P>::close(Primary::Register(error).into(), buffers)
                .map_err(Pending::Buffers),
            Err(current) => Err(Pending::Registration(Registration { current, buffers })),
        };
        Build(result)
    }

    fn provided(
        cause: Self::BufferError,
        ring: Self::Registered,
        buffers: Producer<L>,
    ) -> Self::Build {
        let primary = Primary::Provided(cause).into();
        let result = match ring.try_close() {
            Ok(()) => Pending::<T, L, P>::close(primary, buffers).map_err(Pending::Buffers),
            Err(current) => Err(Pending::Provided(Provided {
                primary,
                current,
                buffers,
            })),
        };
        Build(result)
    }
}

impl<T: token::Codec, L: memory::raw::Canary, P: Copy + From<Primary> + From<memory::Error>>
    create::Build<P> for Build<T, L, P>
{
    type Context = buffers::raw::Context<L>;
    type Cleanup = Resources<T, L, P>;

    fn into_result(self) -> Result<P, Self::Cleanup> {
        self.0.map_err(|pending| Resources(State::Unbound(pending)))
    }

    fn close(self, context: buffers::raw::Context<L>) -> Result<P, Self::Cleanup> {
        match self.0 {
            Ok(primary) => Pending::<T, L, P>::context(primary, context)
                .map_err(|pending| Resources(State::Unbound(Pending::Buffers(pending)))),
            Err(pending) => Err(Resources(State::Bound { pending, context })),
        }
    }
}

impl<T: token::Codec, L: memory::raw::Canary, P: Copy + From<Primary> + From<memory::Error>>
    memory::Cleanup<P> for Resources<T, L, P>
{
    type Error = Fault<T>;

    fn try_close(self, mut report: impl FnMut(Fault<T>)) -> Result<P, Self> {
        match self.0 {
            State::Ring(state) => state
                .try_close(|cause| report(create::Failure::Storage(map::Storage::Ring(cause))))
                .map(|cause| Primary::Ring(cause).into())
                .map_err(|state| Self(State::Ring(state))),
            State::Probe(state) => state
                .try_close(|cause| report(probe_error(cause)))
                .map_err(|state| Self(State::Probe(state))),
            State::Unbound(pending) => pending
                .try_close(&mut report)
                .map_err(|pending| Self(State::Unbound(pending))),
            State::Bound { pending, context } => match pending.try_close(&mut report) {
                Ok(primary) => Pending::<T, L, P>::context(primary, context)
                    .map_err(|pending| Self(State::Unbound(Pending::Buffers(pending)))),
                Err(pending) => Err(Self(State::Bound { pending, context })),
            },
        }
    }

    fn primary(&self) -> P {
        match &self.0 {
            State::Ring(state) => Primary::Ring(state.primary()).into(),
            State::Probe(state) => state.primary(),
            State::Unbound(pending) | State::Bound { pending, .. } => pending.primary(),
        }
    }

    fn failure(&self) -> Fault<T> {
        match &self.0 {
            State::Ring(state) => create::Failure::Storage(map::Storage::Ring(state.failure())),
            State::Probe(state) => probe_error(state.failure()),
            State::Unbound(pending) | State::Bound { pending, .. } => pending.failure(),
        }
    }
}

impl<T: token::Codec, L: memory::raw::Canary, P: Copy + From<Primary> + From<memory::Error>>
    memory::Cleanup<P> for Pending<T, L, P>
{
    type Error = Fault<T>;

    fn try_close(self, mut report: impl FnMut(Fault<T>)) -> Result<P, Self> {
        match self {
            Self::Buffers(state) => Self::retry_buffer(state, &mut report).map_err(Self::Buffers),
            Self::Open(Open { ring, buffers }) => match Self::retry_buffer(buffers, &mut report) {
                Ok(primary) => Self::close_raw(ring, primary).0,
                Err(buffers) => Err(Self::Open(Open { ring, buffers })),
            },
            Self::Raw(Closed { primary, current }) => {
                let (owner, cause) = current.recover().into_parts();
                report(create::Failure::Storage(cause.into()));
                match owner.try_close() {
                    Ok(()) => Ok(primary),
                    Err(current) => Err(Self::Raw(Closed { primary, current })),
                }
            }
            Self::Provided(Provided {
                primary,
                current,
                buffers,
            }) => {
                let (owner, cause) = current.recover().into_parts();
                report(create::Failure::Storage(cause));
                match owner.try_close() {
                    Ok(()) => Self::close(primary, buffers).map_err(Self::Buffers),
                    Err(current) => Err(Self::Provided(Provided {
                        primary,
                        current,
                        buffers,
                    })),
                }
            }
            Self::Registration(Registration { current, buffers }) => match current
                .try_close(|cause| report(create::Failure::Storage(map::Storage::Ring(cause))))
            {
                Ok(cause) => {
                    Self::close(Primary::Register(cause).into(), buffers).map_err(Self::Buffers)
                }
                Err(current) => Err(Self::Registration(Registration { current, buffers })),
            },
        }
    }

    fn primary(&self) -> P {
        match self {
            Self::Buffers(state) => *state.error(),
            Self::Open(Open { buffers, .. }) => *buffers.error(),
            Self::Raw(Closed { primary, .. }) | Self::Provided(Provided { primary, .. }) => {
                *primary
            }
            Self::Registration(Registration { current, .. }) => {
                Primary::Register(current.primary()).into()
            }
        }
    }

    fn failure(&self) -> Fault<T> {
        match self {
            Self::Buffers(state) => create::Failure::Memory(state.value().failure()),
            Self::Open(Open { buffers, .. }) => create::Failure::Memory(buffers.value().failure()),
            Self::Raw(Closed { current, .. }) => create::Failure::Storage(current.failure()),
            Self::Provided(Provided { current, .. }) => create::Failure::Storage(current.failure()),
            Self::Registration(Registration { current, .. }) => {
                create::Failure::Storage(map::Storage::Ring(current.failure()))
            }
        }
    }
}

impl<T: token::Codec, L: memory::raw::Canary, P: Copy + From<Primary> + From<memory::Error>>
    Pending<T, L, P>
{
    fn retry_buffer(
        state: BufferBuild<L, P>,
        report: &mut impl FnMut(Fault<T>),
    ) -> Result<P, BufferBuild<L, P>> {
        let (cleanup, primary) = state.into_parts();
        let (owner, cause) = cleanup.recover().into_parts();
        report(create::Failure::Memory(cause));
        let cleanup = match owner.try_close() {
            Ok(()) => return Ok(primary),
            Err(cleanup) => cleanup,
        };
        Err(BufferBuild::new(cleanup, primary))
    }

    fn close_raw(ring: Ring<T>, primary: P) -> Build<T, L, P> {
        match ring.try_close() {
            Ok(()) => Build(Ok(primary)),
            Err(current) => Build(Err(Self::Raw(Closed { primary, current }))),
        }
    }

    fn close(primary: P, buffers: Producer<L>) -> Result<P, BufferBuild<L, P>> {
        Self::buffers(primary, buffers.try_close().map_err(Cleanup::producer))
    }

    fn context(primary: P, context: buffers::raw::Context<L>) -> Result<P, BufferBuild<L, P>> {
        Self::buffers(primary, context.try_close().map_err(Cleanup::payload))
    }

    fn buffers(primary: P, closed: Result<(), Cleanup<L>>) -> Result<P, BufferBuild<L, P>> {
        let cleanup = match closed {
            Ok(()) => return Ok(primary),
            Err(cleanup) => cleanup,
        };
        Err(BufferBuild::new(cleanup, primary))
    }
}

fn probe_error<S, W>(cause: cleanup::Failure<S, W>) -> create::Failure<S, W> {
    match cause {
        cleanup::Failure::Storage(error) => create::Failure::Storage(error),
        cleanup::Failure::Wait(error) => create::Failure::Wait(error),
    }
}