use alloc::vec::Vec;
use core::cell::RefCell;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use crate::core::error::Result;
use crate::core::line::{CompletionLine, MemoryDescriptor, reached};
use crate::core::time::{Clock, DurNs, MonoNs, Timer};
use crate::error;
pub trait Idle {
fn sleep_until(&mut self, at: MonoNs);
fn park_line<R: Fn(&CompletionLine) -> bool>(
&mut self,
line: &CompletionLine,
ready: R,
bound: DurNs,
) -> bool;
}
struct Source<'l> {
id: u64,
desc: MemoryDescriptor<'l>,
until: Option<MonoNs>,
waker: Waker,
}
struct Alarm {
id: u64,
at: MonoNs,
waker: Waker,
}
#[derive(Default)]
struct Table<'l> {
next_id: u64,
sources: Vec<Source<'l>>,
alarms: Vec<Alarm>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Turn {
Woke,
Slept,
Parked,
}
pub struct Reactor<'l, C: Clock> {
clock: C,
table: RefCell<Table<'l>>,
}
impl<'l, C: Clock> Reactor<'l, C> {
#[must_use]
pub fn new(clock: C) -> Self {
Self {
clock,
table: RefCell::new(Table::default()),
}
}
#[must_use]
pub fn clock(&self) -> &C {
&self.clock
}
#[must_use]
pub fn ready(&self, desc: MemoryDescriptor<'l>, until: Option<MonoNs>) -> Ready<'_, 'l, C> {
Ready {
reactor: self,
desc,
until,
id: None,
}
}
#[must_use]
pub fn pending(&self) -> (usize, usize) {
let t = self.table.borrow();
(t.sources.len(), t.alarms.len())
}
pub fn turn<I: Idle>(&self, idle: &mut I, park_bound: DurNs, tasks_left: usize) -> Result<Turn> {
if self.fire() {
return Ok(Turn::Woke);
}
let now = self.clock.monotonic_ns();
let (deadline, line) = {
let t = self.table.borrow();
let deadline = t
.alarms
.iter()
.map(|a| a.at)
.chain(t.sources.iter().filter_map(|s| s.until))
.min();
(deadline, t.sources.first().map(|s| s.desc.line()))
};
match (line, deadline) {
(None, None) => Err(error!(Stalled, tasks_left)),
(None, Some(at)) => {
idle.sleep_until(at);
Ok(Turn::Slept)
}
(Some(line), deadline) => {
let bound = deadline.map_or(park_bound, |d| park_bound.min(d.since(now)));
let watched: Vec<_> = {
let t = self.table.borrow();
t.sources
.iter()
.filter(|s| core::ptr::eq(s.desc.line(), line))
.map(|s| (s.desc.slot(), s.desc.target()))
.collect()
};
idle.park_line(
line,
|l| watched.iter().any(|&(slot, target)| reached(l, slot, target)),
bound,
);
Ok(Turn::Parked)
}
}
}
pub fn fire(&self) -> bool {
let now = self.clock.monotonic_ns();
let due = {
let mut t = self.table.borrow_mut();
let mut due = Vec::new();
t.sources.retain_mut(|s| {
let fire = s.desc.is_complete() || s.until.is_some_and(|u| now >= u);
if fire {
due.push(s.waker.clone());
}
!fire
});
t.alarms.retain(|a| {
let fire = now >= a.at;
if fire {
due.push(a.waker.clone());
}
!fire
});
due
};
let any = !due.is_empty();
for w in due {
w.wake();
}
any
}
fn register_source(&self, id: &mut Option<u64>, desc: MemoryDescriptor<'l>, until: Option<MonoNs>, waker: &Waker) {
let mut t = self.table.borrow_mut();
if let Some(i) = *id
&& let Some(s) = t.sources.iter_mut().find(|s| s.id == i)
{
s.waker.clone_from(waker);
return;
}
t.next_id += 1;
let i = t.next_id;
*id = Some(i);
t.sources.push(Source {
id: i,
desc,
until,
waker: waker.clone(),
});
}
fn register_alarm(&self, id: &mut Option<u64>, at: MonoNs, waker: &Waker) {
let mut t = self.table.borrow_mut();
if let Some(i) = *id
&& let Some(a) = t.alarms.iter_mut().find(|a| a.id == i)
{
a.waker.clone_from(waker);
return;
}
t.next_id += 1;
let i = t.next_id;
*id = Some(i);
t.alarms.push(Alarm {
id: i,
at,
waker: waker.clone(),
});
}
fn forget(&self, id: Option<u64>) {
if let Some(i) = id {
let mut t = self.table.borrow_mut();
t.sources.retain(|s| s.id != i);
t.alarms.retain(|a| a.id != i);
}
}
}
pub struct Ready<'r, 'l, C: Clock> {
reactor: &'r Reactor<'l, C>,
desc: MemoryDescriptor<'l>,
until: Option<MonoNs>,
id: Option<u64>,
}
impl<C: Clock> Future for Ready<'_, '_, C> {
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<bool> {
let this = self.get_mut();
if this.desc.is_complete() {
this.reactor.forget(this.id.take());
return Poll::Ready(true);
}
if this
.until
.is_some_and(|u| this.reactor.clock.monotonic_ns() >= u)
{
this.reactor.forget(this.id.take());
return Poll::Ready(false);
}
this.reactor
.register_source(&mut this.id, this.desc, this.until, cx.waker());
Poll::Pending
}
}
impl<C: Clock> Drop for Ready<'_, '_, C> {
fn drop(&mut self) {
self.reactor.forget(self.id.take());
}
}
pub struct Alarmed<'r, 'l, C: Clock> {
reactor: &'r Reactor<'l, C>,
at: MonoNs,
id: Option<u64>,
}
impl<C: Clock> Future for Alarmed<'_, '_, C> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let this = self.get_mut();
if this.reactor.clock.monotonic_ns() >= this.at {
this.reactor.forget(this.id.take());
return Poll::Ready(());
}
this.reactor.register_alarm(&mut this.id, this.at, cx.waker());
Poll::Pending
}
}
impl<C: Clock> Drop for Alarmed<'_, '_, C> {
fn drop(&mut self) {
self.reactor.forget(self.id.take());
}
}
impl<'l, C: Clock> Reactor<'l, C> {
#[must_use]
pub fn alarm(&self, at: MonoNs) -> Alarmed<'_, 'l, C> {
Alarmed {
reactor: self,
at,
id: None,
}
}
}
impl<C: Clock> Timer for Reactor<'_, C> {
async fn sleep_until(&self, at: MonoNs) {
self.alarm(at).await;
}
}
impl<C: Clock> Clock for Reactor<'_, C> {
fn monotonic_ns(&self) -> MonoNs {
self.clock.monotonic_ns()
}
}
#[cfg(test)]
#[path = "reactor_tests.rs"]
pub(crate) mod tests;