dope-runtime 0.29.0

Thread-per-core execution and process lifecycle for dope
Documentation
use std::{mem, time};

use dope_core::{
    driver,
    driver::{
        life, schedule,
        schedule::{clock, ready, state},
    },
    io::Event,
};
use drive::turn;
use o3::base::cell::brand;
use state::scope;
use turn::{
    modes,
    ready::{Ready, RootReady},
    routes,
};

use crate::executor::{self, drive, drive::clock::Time};

pub(crate) struct Scope<'run, 'driver, 'app, 'd: 'app, D: executor::raw::Layout<'d>, S> {
    driver: &'run mut driver::Context<'driver, 'd>,
    view: executor::raw::View<'app, 'd, D, S, D::ParkPlan>,
    token: &'run mut brand::Token<brand::Identity<'d>>,
    schedule: scope::Scope<'driver, 'd>,
    pending: &'run mut Option<Event<'d>>,
}

const _: () = {
    assert!(mem::size_of::<Boot<'_, '_, '_>>() == 3 * mem::size_of::<usize>());
    assert!(mem::align_of::<Boot<'_, '_, '_>>() == mem::align_of::<usize>());
};

/// Exclusive startup authority admitted without unresolved application completions.
pub(crate) struct Boot<'run, 'driver, 'd> {
    driver: &'run mut driver::Context<'driver, 'd>,
    schedule: scope::Scope<'driver, 'd>,
    pending: &'run mut Option<Event<'d>>,
    mode: modes::Boot,
}

pub(crate) struct Core<'turn, 'd, M> {
    pub(super) driver: driver::Context<'turn, 'd>,
    pub(super) turn: scope::Guard<'turn, 'd>,
    pub(super) pending: &'turn mut Option<Event<'d>>,
    pub(super) mode: M,
}

impl<'run, 'driver, 'app, 'd: 'app, D, S> Scope<'run, 'driver, 'app, 'd, D, S>
where
    D: executor::raw::App<'d>,
{
    pub(crate) const fn new(
        driver: &'run mut driver::Context<'driver, 'd>,
        view: executor::raw::View<'app, 'd, D, S, D::ParkPlan>,
        token: &'run mut brand::Token<brand::Identity<'d>>,
        schedule: scope::Scope<'driver, 'd>,
        pending: &'run mut Option<Event<'d>>,
    ) -> Self {
        Self {
            driver,
            view,
            token,
            schedule,
            pending,
        }
    }

    pub(crate) fn next_app(
        &mut self,
        tick: clock::Sample,
        shutdown: impl FnMut() -> turn::Status,
    ) -> Result<Ready<'_, 'd, modes::App<'_, 'app, 'd, D, S>>, drive::Error> {
        Ready::new(self.begin(tick), shutdown)
    }

    fn begin(&mut self, tick: clock::Sample) -> Core<'_, 'd, modes::App<'_, 'app, 'd, D, S>> {
        let turn = self.schedule.begin_at(tick);
        Core {
            driver: self.driver.reborrow(),
            turn,
            pending: &mut *self.pending,
            mode: modes::App {
                view: self.view,
                token: &mut *self.token,
            },
        }
    }

    pub(crate) fn driver_ref(&self) -> driver::Reference<'d> {
        self.driver.driver_ref()
    }

    pub(crate) fn next_root<'turn>(
        &'turn mut self,
        root: &ready::Slot<'d, routes::Wake>,
        tick: clock::Sample,
        shutdown: impl FnMut() -> turn::Status,
    ) -> Result<RootReady<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>, drive::Error> {
        RootReady::new_app(self.begin(tick), root, shutdown)
    }
}

impl<'run, 'driver, 'd> Boot<'run, 'driver, 'd> {
    pub(crate) fn try_new(
        driver: &'run mut driver::Context<'driver, 'd>,
        schedule: scope::Scope<'driver, 'd>,
        pending: &'run mut Option<Event<'d>>,
        admitted: Result<(), life::Busy>,
    ) -> Result<Self, drive::Error> {
        if pending.is_some() {
            return Err(life::Busy.into());
        }
        admitted?;
        Ok(Self {
            mode: modes::Boot,
            driver,
            schedule,
            pending,
        })
    }

    fn begin(&mut self, tick: clock::Sample) -> Core<'_, 'd, modes::Boot> {
        Core {
            driver: self.driver.reborrow(),
            turn: self.schedule.begin_at(tick),
            pending: &mut *self.pending,
            mode: self.mode,
        }
    }

    pub(crate) fn driver_ref(&self) -> driver::Reference<'d> {
        self.driver.driver_ref()
    }

    pub(crate) fn next_root<'turn>(
        &'turn mut self,
        root: &ready::Slot<'d, routes::Wake>,
        tick: clock::Sample,
        shutdown: impl FnMut() -> turn::Status,
    ) -> Result<RootReady<'turn, 'd, modes::Boot>, drive::Error> {
        RootReady::new_boot(self.begin(tick), root, shutdown)
    }
}

impl<'app, 'd: 'app, D, S> Core<'_, 'd, modes::App<'_, 'app, 'd, D, S>>
where
    D: executor::raw::App<'d>,
{
    pub(super) fn shutdown(&self) -> schedule::Progress<'d> {
        match self
            .mode
            .view
            .stopping(
                self.mode.token,
                self.driver.regions(),
                self.driver.driver_ref().clock(),
            )
            .reduce(self.maintenance())
        {
            state @ schedule::Progress::Waiting(_) => state.reduce(self.timer()),
            state => state,
        }
    }
}

impl<'d, M> Core<'_, 'd, M>
where
    M: modes::Mode<'d>,
{
    pub(super) fn timer(&self) -> schedule::Progress<'d> {
        match self.driver.driver_ref().timer().earliest() {
            Some(due) => schedule::Progress::until(due),
            None => schedule::Progress::Quiescent,
        }
    }

    pub(super) fn maintenance(&self) -> schedule::Progress<'d> {
        self.driver.driver_ref().maintenance()
    }

    pub(super) fn timeout(
        &self,
        state: schedule::Progress<'d>,
        source: impl Time,
    ) -> Option<time::Duration> {
        if matches!(state, schedule::Progress::Runnable) || !self.is_quiet() {
            return Some(time::Duration::ZERO);
        }
        match state {
            schedule::Progress::Waiting(wait) => wait
                .deadline()
                .map(|due| due.into_raw().saturating_duration_since(source.park())),
            _ => None,
        }
    }

    pub(super) fn is_quiet(&self) -> bool {
        !self.turn.is_exhausted() && !self.driver.driver_ref().ready().has_ready()
    }
}