cartel-sqlite 0.30.0

SQLite driver for the dope runtime
use crate::{config, sys};
use dope_core::driver::{
    schedule::clock::{self, timer},
    schedule::ready::wake,
};
use std::{mem, task};

pub(crate) struct Slot<'d> {
    alarm: Option<timer::Alarm<'d>>,
    state: State<'d>,
}

#[derive(Clone, Copy)]
enum State<'d> {
    Idle,
    Ready(Budget<'d>),
    PendingYield(Budget<'d>),
    PendingSleep(Budget<'d>),
}

#[derive(Clone, Copy)]
struct Budget<'d> {
    deadline: clock::Deadline<'d>,
    fault: sys::Fault,
    remaining: u32,
}

pub(crate) enum Wait {
    Ready,
    Pending,
}

impl<'d> Slot<'d> {
    pub(crate) const fn yielding() -> Self {
        Self {
            alarm: None,
            state: State::Idle,
        }
    }

    pub(crate) const fn timed(alarm: timer::Alarm<'d>) -> Self {
        Self {
            alarm: Some(alarm),
            state: State::Idle,
        }
    }

    pub(super) fn defer(
        &mut self,
        limits: &config::Contention,
        now: clock::Moment<'d>,
        fault: sys::Fault,
        wake: wake::Waker<'d>,
    ) -> task::Poll<Result<(), crate::Error>> {
        let mut budget = match self.state {
            State::Idle => Budget {
                deadline: now.clamp_add(limits.window),
                fault,
                remaining: limits.retries.get(),
            },
            State::Ready(mut budget) => {
                self.state = State::Idle;
                budget.fault = fault;
                budget
            }
            State::PendingYield(_) | State::PendingSleep(_) => return self.poll(now, wake),
        };
        let Some(left) = budget.remaining.checked_sub(1) else {
            return task::Poll::Ready(Err(fault.busy()));
        };
        budget.remaining = left;
        if budget.deadline.is_due(now) {
            return task::Poll::Ready(Err(fault.timeout()));
        }
        if let Some(alarm) = &self.alarm
            && let config::Delay::Sleep(interval) = limits.delay()
        {
            let wake_at = now.clamp_add(interval).min(budget.deadline);
            alarm.arm(wake_at, wake);
            self.state = State::PendingSleep(budget);
        } else {
            self.state = State::PendingYield(budget);
            wake.wake();
        }
        task::Poll::Pending
    }

    pub(super) const fn wait(&self) -> Wait {
        match &self.state {
            State::Idle | State::Ready(_) => Wait::Ready,
            State::PendingYield(_) | State::PendingSleep(_) => Wait::Pending,
        }
    }

    pub(super) fn poll(
        &mut self,
        now: clock::Moment<'d>,
        wake: wake::Waker<'d>,
    ) -> task::Poll<Result<(), crate::Error>> {
        let (budget, alarm) = match &self.state {
            State::PendingYield(budget) => (budget, None),
            State::PendingSleep(budget) => (budget, self.alarm.as_ref()),
            State::Idle | State::Ready(_) => return task::Poll::Ready(Ok(())),
        };
        if budget.deadline.is_due(now) {
            if let Some(alarm) = alarm {
                alarm.clear();
            }
            let fault = budget.fault;
            self.state = State::Idle;
            return task::Poll::Ready(Err(fault.timeout()));
        }
        if let Some(alarm) = alarm
            && alarm.poll(now, wake).is_pending()
        {
            return task::Poll::Pending;
        }
        self.state = State::Ready(*budget);
        task::Poll::Ready(Ok(()))
    }

    pub(crate) fn finish(&mut self) {
        if let Some(alarm) = &self.alarm {
            alarm.clear();
        }
        self.state = State::Idle;
    }
}

const _: () = {
    assert!(
        mem::size_of::<Option<timer::Alarm<'static>>>() == mem::size_of::<timer::Alarm<'static>>()
    );
    assert!(
        mem::size_of::<State<'static>>()
            <= mem::size_of::<Budget<'static>>() + mem::align_of::<Budget<'static>>()
    );
    assert!(
        mem::size_of::<Slot<'static>>()
            == mem::size_of::<timer::Alarm<'static>>() + mem::size_of::<State<'static>>()
    );
    assert!(mem::align_of::<Slot<'static>>() == mem::align_of::<timer::Alarm<'static>>());
};