dope-core 0.15.0

The manifold runtime
Documentation
use std::{io, os::fd, ptr};

use crate::{
    backend::{
        self,
        kqueue::engine::{event, read},
    },
    driver::flight,
    io::fd::handles,
};

enum RecvKind {
    Bytes,
    Message,
}

#[repr(transparent)]
pub(crate) struct Arm<'a> {
    backend: &'a mut backend::Kqueue,
}

impl<'a> Arm<'a> {
    pub(in crate::backend::kqueue::engine) fn new(backend: &'a mut backend::Kqueue) -> Self {
        Self { backend }
    }
}

impl Arm<'_> {
    fn arm_read_multi(&mut self, raw: fd::RawFd, udata: event::Udata) -> io::Result<()> {
        if !self.backend.poll.changes.try_upsert(libc::kevent {
            ident: raw as libc::uintptr_t,
            filter: libc::EVFILT_READ,
            flags: libc::EV_ADD | libc::EV_ENABLE | libc::EV_DISPATCH,
            fflags: 0,
            data: 0,
            udata: udata.into_kevent(),
        }) {
            return Err(io::Error::new(
                io::ErrorKind::WouldBlock,
                "dope: kqueue changelist capacity exhausted",
            ));
        }
        Ok(())
    }

    pub(in crate::backend::kqueue::engine) fn re_enable_read(
        &mut self,
        raw: fd::RawFd,
        udata: event::Udata,
    ) {
        assert!(self.backend.poll.changes.try_upsert(libc::kevent {
            ident: raw as libc::uintptr_t,
            filter: libc::EVFILT_READ,
            flags: libc::EV_ENABLE | libc::EV_DISPATCH,
            fflags: 0,
            data: 0,
            udata: udata.into_kevent(),
        }));
    }

    pub(in crate::backend::kqueue::engine) fn disarm_filter(&mut self, fd: fd::RawFd, filter: i16) {
        use std::ptr::null_mut;
        if filter == 0 {
            return;
        }
        assert!(self.backend.poll.changes.try_upsert(libc::kevent {
            ident: fd as libc::uintptr_t,
            filter,
            flags: libc::EV_DELETE,
            fflags: 0,
            data: 0,
            udata: null_mut(),
        }));
    }

    pub(in crate::backend::kqueue::engine) fn arm_accept_oneshot_inner(
        &mut self,
        ud: flight::raw::Echo,
        fixed: handles::FixedSlot,
        fd: fd::RawFd,
        addr_ptr: *mut libc::sockaddr,
        addrlen_ptr: *mut libc::socklen_t,
    ) -> bool {
        let slot = read::Slot::Accept(read::AcceptSlot {
            hdr: read::SlotHeader { fd, fixed, ud },
            addr_ptr,
            addrlen_ptr,
            oneshot: true,
        });
        let Some(read) = self.backend.reads.install(slot) else {
            return false;
        };
        if self.arm_read_multi(fd, event::Udata::accept(ud)).is_err() {
            let removed = self.backend.reads.remove_active(read);
            debug_assert_eq!(removed, Some(fd));
            return false;
        }
        true
    }

    pub(in crate::backend::kqueue::engine) fn arm_accept_multishot_inner(
        &mut self,
        ud: flight::raw::Echo,
        fixed: handles::FixedSlot,
        fd: fd::RawFd,
    ) -> bool {
        let slot = read::Slot::Accept(read::AcceptSlot {
            hdr: read::SlotHeader { fd, fixed, ud },
            addr_ptr: ptr::null_mut(),
            addrlen_ptr: ptr::null_mut(),
            oneshot: false,
        });
        let Some(read) = self.backend.reads.install(slot) else {
            return false;
        };
        if self.arm_read_multi(fd, event::Udata::accept(ud)).is_err() {
            let removed = self.backend.reads.remove_active(read);
            debug_assert_eq!(removed, Some(fd));
            return false;
        }
        true
    }

    pub(in crate::backend::kqueue::engine) fn cancel_accept_inner(
        &mut self,
        ud: flight::raw::Echo,
    ) -> bool {
        self.cancel_read_inner(ud, read::Family::Accept)
    }

    fn arm_recv_inner(
        &mut self,
        ud: flight::raw::Echo,
        slot: handles::FixedSlot,
        kind: RecvKind,
    ) -> bool {
        let Some(raw) = self.backend.files.raw(slot) else {
            self.backend.push_pending(event::Completion::RecvControl {
                ud,
                result: -libc::EBADF,
                more: false,
            });
            return true;
        };
        let header = read::SlotHeader {
            fd: raw,
            fixed: slot,
            ud,
        };
        let (udata, read) = match kind {
            RecvKind::Bytes => (event::Udata::recv(ud), read::Slot::Recv(header)),
            RecvKind::Message => (event::Udata::recv_msg(ud), read::Slot::RecvMsg(header)),
        };
        let Some(read) = self.backend.reads.install(read) else {
            return false;
        };
        if self.arm_read_multi(raw, udata).is_err() {
            let removed = self.backend.reads.remove_active(read);
            debug_assert_eq!(removed, Some(raw));
            return false;
        }
        true
    }

    pub(in crate::backend::kqueue::engine) fn arm_recv_multi_inner(
        &mut self,
        ud: flight::raw::Echo,
        slot: handles::FixedSlot,
    ) -> bool {
        self.arm_recv_inner(ud, slot, RecvKind::Bytes)
    }

    pub(in crate::backend::kqueue::engine) fn arm_recv_msg_multi_inner(
        &mut self,
        ud: flight::raw::Echo,
        slot: handles::FixedSlot,
    ) -> bool {
        self.arm_recv_inner(ud, slot, RecvKind::Message)
    }

    pub(in crate::backend::kqueue::engine) fn cancel_recv_inner(
        &mut self,
        ud: flight::raw::Echo,
    ) -> bool {
        self.cancel_read_inner(ud, read::Family::Recv)
    }

    fn cancel_read_inner(&mut self, ud: flight::raw::Echo, family: read::Family) -> bool {
        let slot_idx = event::Udata::read_key(ud);
        let found = self.backend.reads.slots.get(&slot_idx).is_some_and(|slot| {
            let header = slot.header();
            slot.family() == family && header.ud == ud
        });
        if !found {
            return true;
        }
        if self.backend.pending.is_full() {
            return false;
        }
        let Some(slot) = self.backend.reads.remove(slot_idx) else {
            return true;
        };
        let fd = slot.header().fd;
        self.disarm_filter(fd, libc::EVFILT_READ);
        match family {
            read::Family::Accept => self.backend.push_pending(event::Completion::AcceptFailure {
                ud,
                errno: libc::ECANCELED,
                more: false,
            }),
            read::Family::Recv => self.backend.push_pending(event::Completion::RecvControl {
                ud,
                result: -libc::ECANCELED,
                more: false,
            }),
        }
        true
    }
}