dope-core 0.10.0

The manifold runtime
Documentation
use std::mem;

use crate::backend::uring::sqe;
use crate::driver::token::SlotIndex;
use crate::io::fd::FdSlot;
use o3::collections::FixedQueue;

#[derive(Clone, Copy)]
enum FileState {
    Empty,
    Live,
    Creating { user_data: u64, close_pending: bool },
    Closing,
}

pub(crate) enum Admission {
    Start,
    Defer,
    Reject,
}

pub(crate) struct FileTable {
    state: Box<[FileState]>,
    pending: Box<[Option<(sqe::Create, sqe::Sqe)>]>,
    ready: FixedQueue<FdSlot>,
    deferred_close: FixedQueue<FdSlot>,
}

impl FileTable {
    pub(super) fn new(slots: usize) -> Self {
        Self {
            state: (0..slots).map(|_| FileState::Empty).collect(),
            pending: (0..slots).map(|_| None).collect(),
            ready: FixedQueue::with_capacity(slots),
            deferred_close: FixedQueue::with_capacity(slots),
        }
    }

    pub(crate) fn admission(&self, slot: FdSlot) -> Admission {
        let index = slot.raw() as usize;
        let Some(state) = self.state.get(index).copied() else {
            return Admission::Reject;
        };
        match state {
            FileState::Empty => Admission::Start,
            FileState::Closing if self.pending[index].is_none() => Admission::Defer,
            FileState::Creating {
                close_pending: true,
                ..
            } if self.pending[index].is_none() => Admission::Defer,
            FileState::Live | FileState::Creating { .. } | FileState::Closing => Admission::Reject,
        }
    }

    pub(crate) fn begin_create(&mut self, create: sqe::Create) {
        debug_assert!(matches!(
            self.state[create.slot.raw() as usize],
            FileState::Empty
        ));
        self.state[create.slot.raw() as usize] = FileState::Creating {
            user_data: create.user_data,
            close_pending: false,
        };
    }

    pub(crate) fn defer_create(&mut self, create: sqe::Create, sqe: sqe::Sqe) {
        self.pending[create.slot.raw() as usize] = Some((create, sqe));
    }

    pub(crate) fn set_live(&mut self, slot: FdSlot) {
        self.state[slot.raw() as usize] = FileState::Live;
    }

    pub(super) fn complete_create(&mut self, slot: SlotIndex, result: i32) -> Option<u64> {
        let index = slot.raw() as usize;
        let state = self.state.get_mut(index)?;
        let FileState::Creating {
            user_data,
            close_pending,
        } = mem::replace(state, FileState::Empty)
        else {
            return None;
        };
        if result >= 0 {
            if close_pending {
                *state = FileState::Closing;
                let Some(entry) = self.deferred_close.vacant_entry() else {
                    unreachable!()
                };
                entry.push_back(FdSlot::new(index as u32));
            } else {
                *state = FileState::Live;
            }
        } else if self.pending[index].is_some() {
            let Some(entry) = self.ready.vacant_entry() else {
                unreachable!()
            };
            entry.push_back(FdSlot::new(index as u32));
        }
        Some(user_data)
    }

    pub(super) fn complete_close(&mut self, slot: SlotIndex) {
        let index = slot.raw() as usize;
        let Some(state) = self.state.get_mut(index) else {
            return;
        };
        debug_assert!(matches!(*state, FileState::Closing));
        *state = FileState::Empty;
        if self.pending[index].is_some() {
            let Some(entry) = self.ready.vacant_entry() else {
                unreachable!()
            };
            entry.push_back(FdSlot::new(index as u32));
        }
    }

    pub(super) fn mark_accepted(&mut self, result: i32, close_pending: bool) {
        if result < 0 {
            return;
        }
        if let Some(state) = self.state.get_mut(result as usize) {
            debug_assert!(matches!(*state, FileState::Empty));
            if close_pending {
                *state = FileState::Closing;
                let Some(entry) = self.deferred_close.vacant_entry() else {
                    unreachable!()
                };
                entry.push_back(FdSlot::new(result as u32));
            } else {
                *state = FileState::Live;
            }
        }
    }

    pub(super) fn flush_ready(&mut self, mut push: impl FnMut(&sqe::Sqe) -> bool) {
        while let Some(&slot) = self.ready.front() {
            let index = slot.raw() as usize;
            let Some((create, sqe)) = self.pending[index].take() else {
                self.ready.pop_front();
                continue;
            };
            if !push(&sqe) {
                self.pending[index] = Some((create, sqe));
                break;
            }
            self.begin_create(create);
            self.ready.pop_front();
        }
    }

    pub(super) fn flush_deferred_close(&mut self, mut push_close: impl FnMut(FdSlot) -> bool) {
        while let Some(&slot) = self.deferred_close.front() {
            if !push_close(slot) {
                break;
            }
            self.deferred_close.pop_front();
        }
    }

    pub(super) fn release(&mut self, slot: FdSlot, mut push_close: impl FnMut(FdSlot) -> bool) {
        let index = slot.raw() as usize;
        let Some(state) = self.state.get(index).copied() else {
            return;
        };
        match state {
            FileState::Empty | FileState::Closing => {
                self.pending[index] = None;
            }
            FileState::Live => {
                self.state[index] = FileState::Closing;
                self.flush_deferred_close(&mut push_close);
                if !push_close(slot) {
                    let Some(entry) = self.deferred_close.vacant_entry() else {
                        unreachable!()
                    };
                    entry.push_back(slot);
                }
            }
            FileState::Creating {
                user_data,
                close_pending: false,
            } => {
                self.state[index] = FileState::Creating {
                    user_data,
                    close_pending: true,
                };
            }
            FileState::Creating {
                close_pending: true,
                ..
            } => {
                self.pending[index] = None;
            }
        }
    }
}