use alloc::boxed::Box;
use alloc::collections::VecDeque;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Poll {
Pending,
Ready,
}
pub trait Task {
fn poll(&mut self) -> Poll;
}
struct Entry {
id: u64,
task: Box<dyn Task>,
}
#[derive(Default)]
pub struct Scheduler {
ready: VecDeque<Entry>,
next_id: u64,
}
impl Scheduler {
pub fn new() -> Self {
Self {
ready: VecDeque::new(),
next_id: 0,
}
}
pub fn spawn(&mut self, task: Box<dyn Task>) -> u64 {
let id = self.next_id;
self.next_id += 1;
self.ready.push_back(Entry { id, task });
id
}
pub fn task_count(&self) -> usize {
self.ready.len()
}
pub fn tick(&mut self) -> Option<(u64, Poll)> {
let mut entry = self.ready.pop_front()?;
let result = entry.task.poll();
let id = entry.id;
if result == Poll::Pending {
self.ready.push_back(entry);
}
Some((id, result))
}
pub fn run_to_completion(&mut self) -> usize {
let mut ticks = 0;
while self.tick().is_some() {
ticks += 1;
}
ticks
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::rc::Rc;
use core::cell::RefCell;
struct CountdownTask {
remaining: u32,
log: Rc<RefCell<alloc::vec::Vec<u64>>>,
id: u64,
}
impl Task for CountdownTask {
fn poll(&mut self) -> Poll {
self.log.borrow_mut().push(self.id);
if self.remaining == 0 {
Poll::Ready
} else {
self.remaining -= 1;
Poll::Pending
}
}
}
#[test]
fn runs_all_tasks_to_completion() {
let mut s = Scheduler::new();
let log = Rc::new(RefCell::new(alloc::vec::Vec::new()));
s.spawn(Box::new(CountdownTask {
remaining: 2,
log: log.clone(),
id: 0,
}));
s.spawn(Box::new(CountdownTask {
remaining: 1,
log: log.clone(),
id: 1,
}));
let ticks = s.run_to_completion();
assert_eq!(s.task_count(), 0);
assert!(ticks >= 5);
let l = log.borrow();
assert!(l.contains(&0) && l.contains(&1));
}
#[test]
fn immediate_ready_task_finishes_in_one_tick() {
struct Done;
impl Task for Done {
fn poll(&mut self) -> Poll {
Poll::Ready
}
}
let mut s = Scheduler::new();
let id = s.spawn(Box::new(Done));
assert_eq!(s.tick(), Some((id, Poll::Ready)));
assert_eq!(s.task_count(), 0);
assert_eq!(s.tick(), None);
}
}