use std::{ops::ControlFlow, pin, task};
use dope_core::{
driver,
driver::{
result::poll,
schedule,
schedule::{ready, ready::Target},
},
io::{Event, event::Kind},
};
use drive::turn;
use turn::{modes, routes, scope::Core};
use crate::executor::{self, boot, drive, root};
const READY_ROUNDS: usize = 2;
#[must_use]
pub(crate) struct Ready<'turn, 'd, M> {
pub(super) core: Core<'turn, 'd, M>,
pub(super) events: poll::Drain,
pub(super) status: turn::Status,
}
pub(crate) struct RootReady<'turn, 'd, M> {
ready: Ready<'turn, 'd, M>,
root: ready::Readiness,
}
impl<'turn, 'app, 'd: 'app, D, S> Ready<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>
where
D: executor::raw::App<'d>,
{
pub(super) fn new(
core: Core<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>,
shutdown: impl FnMut() -> turn::Status,
) -> Result<Self, drive::Error> {
let mut ready = Self::observe(core, shutdown)?;
ready.activate(None);
Ok(ready)
}
fn observe(
mut core: Core<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>,
mut shutdown: impl FnMut() -> turn::Status,
) -> Result<Self, drive::Error> {
let mut status = turn::Status::Active;
let events = {
let (lane, work) = core.turn.split();
let held = match core.pending.take() {
None => ControlFlow::Continue(()),
Some(event) => Self::dispatch(
event,
&mut core.mode,
&mut core.driver,
work.reborrow(),
&mut status,
&mut shutdown,
),
};
match held {
ControlFlow::Break(event) => {
*core.pending = Some(event);
poll::Drain::Pending
}
ControlFlow::Continue(()) => {
let batch =
driver::Context::dispatch(&mut core.driver, lane, |event, driver| {
Self::dispatch(
event,
&mut core.mode,
driver,
work.reborrow(),
&mut status,
&mut shutdown,
)
});
match batch {
poll::Dispatch::Ready { drain, retained } => {
*core.pending = retained;
drain
}
poll::Dispatch::Fault(fault) => return Err(fault.into()),
}
}
}
};
Ok(Self {
core,
events,
status,
})
}
fn dispatch(
event: Event<'d>,
mode: &mut modes::App<'turn, 'app, 'd, D, S>,
driver: &mut driver::Context<'_, 'd>,
work: schedule::Turn<'_, 'd>,
status: &mut turn::Status,
shutdown: &mut impl FnMut() -> turn::Status,
) -> ControlFlow<Event<'d>> {
match event.into_kind() {
Kind::Shutdown => {
*status = shutdown();
ControlFlow::Continue(())
}
event => mode.view.dispatch(mode.token, event.into(), work, driver),
}
}
fn activate(
&mut self,
mut root: Option<(&ready::Slot<'d, routes::Wake>, &mut ready::Readiness)>,
) {
let work = self.core.turn.turn();
let mut rounds = self.core.turn.ready();
let mut left = READY_ROUNDS;
while left != 0 {
left -= 1;
let step = rounds.drain(|target| {
self.core.mode.view.activate(
self.core.mode.token,
target,
work.reborrow(),
&mut self.core.driver.reborrow(),
);
});
if step.is_none() {
break;
}
if let Some((slot, observed)) = root.as_mut()
&& let ready::Readiness::Ready = slot.take()
{
**observed = ready::Readiness::Ready;
}
if rounds.is_exhausted() {
break;
}
}
}
}
impl<'turn, 'd, M> Ready<'turn, 'd, M>
where
M: modes::Mode<'d>,
{
pub(crate) fn park(mut self) -> turn::Parked<'turn, 'd, M> {
M::park(&mut self.core);
turn::Parked::new(self.core, self.events, self.status)
}
pub(crate) fn sync(mut self) -> Result<poll::Commit, poll::Error> {
let work = self.core.turn.reactor();
driver::Context::commit(&mut self.core.driver, work)
}
pub(crate) fn is_quiet(&self) -> bool {
self.core.pending.is_none()
&& matches!(self.events, poll::Drain::Drained)
&& self.core.is_quiet()
}
}
impl<'app, 'd: 'app, D, S> Ready<'_, 'd, modes::App<'_, 'app, 'd, D, S>>
where
D: executor::raw::App<'d>,
{
pub(crate) fn begin_drain(mut self)
where
D: executor::raw::Drain<'d>,
{
self.core.mode.view.begin_drain(
self.core.mode.token,
self.core.turn.turn(),
self.core.driver.reborrow(),
);
}
pub(crate) fn force_drain(mut self)
where
D: executor::raw::Drain<'d>,
{
self.core.mode.view.force_drain(
self.core.mode.token,
self.core.turn.turn(),
self.core.driver.reborrow(),
);
}
pub(crate) fn shutdown(&self) -> schedule::Progress<'d> {
self.core.shutdown()
}
}
impl<'turn, 'app, 'd: 'app, D, S> RootReady<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>
where
D: executor::raw::App<'d>,
{
pub(super) fn new_app(
core: Core<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>,
root: &ready::Slot<'d, routes::Wake>,
shutdown: impl FnMut() -> turn::Status,
) -> Result<Self, drive::Error> {
let admission = core.turn.turn().app().lease();
let mut ready = Ready::observe(core, shutdown)?;
let mut observed = root.take();
ready.activate(Some((root, &mut observed)));
drop(admission);
Ok(Self {
ready,
root: observed,
})
}
}
impl<'turn, 'd> RootReady<'turn, 'd, modes::Boot> {
pub(super) fn new_boot(
mut core: Core<'turn, 'd, modes::Boot>,
root: &ready::Slot<'d, routes::Wake>,
mut shutdown: impl FnMut() -> turn::Status,
) -> Result<Self, drive::Error> {
let mut status = turn::Status::Active;
let events = {
let lane = core.turn.reactor();
let batch = driver::Context::dispatch(&mut core.driver, lane, |event, _source| {
match event.into_kind() {
Kind::Shutdown => {
status = shutdown();
ControlFlow::Continue(())
}
event => ControlFlow::Break(event.into()),
}
});
match batch {
poll::Dispatch::Ready { drain, retained } => {
*core.pending = retained;
drain
}
poll::Dispatch::Fault(fault) => return Err(fault.into()),
}
};
let root = root.take();
Ok(Self {
ready: Ready {
core,
events,
status,
},
root,
})
}
pub(crate) fn poll_boot<R>(
self,
root: R,
wake: Target<'d>,
cause: boot::Cause,
) -> (Ready<'turn, 'd, modes::Boot>, boot::Poll<R, R::Output>)
where
R: boot::Root<'d>,
{
let context = boot::Context::new(wake, self.ready.core.turn.turn().app(), cause);
let output = R::poll(root, context);
(self.skip(), output)
}
}
impl<'turn, 'd, M> RootReady<'turn, 'd, M>
where
M: modes::Mode<'d>,
{
pub(crate) const fn is_root(&self) -> bool {
matches!(self.root, ready::Readiness::Ready)
}
pub(crate) fn skip(self) -> Ready<'turn, 'd, M> {
self.ready
}
}
impl<'turn, 'app, 'd: 'app, D, S> RootReady<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>
where
D: executor::raw::App<'d>,
{
pub(crate) fn poll<P>(
mut self,
root: pin::Pin<&mut P>,
wake: Target<'d>,
) -> (
Ready<'turn, 'd, modes::App<'turn, 'app, 'd, D, S>>,
task::Poll<P::Output>,
)
where
P: root::Root<'d>,
{
let core = &mut self.ready.core;
let output = P::poll(root::Context::new(
root,
wake,
core.turn.turn().app(),
core.driver.reborrow(),
));
(self.skip(), output)
}
}