#![no_std]
extern crate task;
extern crate sync_irq;
extern crate alloc;
#[macro_use] extern crate lazy_static;
extern crate time;
extern crate crossbeam_utils;
use core::task::Waker;
use alloc::collections::binary_heap::BinaryHeap;
use sync_irq::IrqSafeMutex;
use task::{get_my_current_task, TaskRef, RunState};
use crossbeam_utils::atomic::AtomicCell;
use time::{now, Instant, Monotonic};
pub use time::Duration;
#[derive(Clone)]
struct SleepingTaskNode {
resume_time: Instant,
action: Action,
}
#[derive(Clone)]
enum Action {
Sync(TaskRef),
Async(Waker),
}
impl Action {
fn act(self) {
match self {
Action::Sync(task) => {
task.unblock().expect("failed to unblock sleeping task");
},
Action::Async(waker) => waker.wake(),
}
}
}
impl Eq for SleepingTaskNode {}
impl PartialEq for SleepingTaskNode {
fn eq(&self, other: &Self) -> bool {
self.resume_time == other.resume_time
}
}
impl Ord for SleepingTaskNode {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
other.resume_time.cmp(&self.resume_time)
}
}
impl PartialOrd for SleepingTaskNode {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
lazy_static! {
static ref DELAYED_TASKLIST: IrqSafeMutex<BinaryHeap<SleepingTaskNode>>
= IrqSafeMutex::new(BinaryHeap::new());
}
static NEXT_DELAYED_TASK_UNBLOCK_TIME: AtomicCell<Instant> = AtomicCell::new(Instant::MAX);
fn add_to_delayed_tasklist(new_node: SleepingTaskNode) {
let SleepingTaskNode { resume_time, .. } = new_node;
DELAYED_TASKLIST.lock().push(new_node);
let next_unblock_time = NEXT_DELAYED_TASK_UNBLOCK_TIME.load();
if resume_time < next_unblock_time {
NEXT_DELAYED_TASK_UNBLOCK_TIME.store(resume_time);
}
}
fn remove_next_task_from_delayed_tasklist() {
let mut delayed_tasklist = DELAYED_TASKLIST.lock();
if let Some(SleepingTaskNode { action, .. }) = delayed_tasklist.pop() {
action.act();
match delayed_tasklist.peek() {
Some(SleepingTaskNode { resume_time, .. }) =>
NEXT_DELAYED_TASK_UNBLOCK_TIME.store(*resume_time),
None => NEXT_DELAYED_TASK_UNBLOCK_TIME.store(Instant::MAX),
}
}
}
pub fn unblock_sleeping_tasks() {
let time = now::<Monotonic>();
while time > NEXT_DELAYED_TASK_UNBLOCK_TIME.load() {
remove_next_task_from_delayed_tasklist();
}
}
pub fn sleep(duration: Duration) -> Result<(), RunState> {
let current_time = now::<Monotonic>();
let resume_time = current_time + duration;
let current_task = get_my_current_task().unwrap();
add_to_delayed_tasklist(SleepingTaskNode{action: Action::Sync(current_task.clone()), resume_time});
current_task.block()?;
task::schedule();
Ok(())
}
pub fn sleep_until(resume_time: Instant) -> Result<(), RunState> {
let current_time = now::<Monotonic>();
if resume_time > current_time {
sleep(resume_time - current_time)?;
}
Ok(())
}
pub mod future {
use core::task::Poll;
use super::*;
pub fn sleep(duration: Duration, waker: Waker) {
let current_time = now::<Monotonic>();
let resume_time = current_time + duration;
add_to_delayed_tasklist(
SleepingTaskNode {
action: Action::Async(waker),
resume_time,
}
);
}
pub fn sleep_until(resume_time: Instant, waker: &Waker) -> Poll<()> {
let current_time = now::<Monotonic>();
if let Some(duration) = resume_time.checked_duration_since(current_time) {
sleep(duration, waker.clone());
Poll::Pending
} else {
Poll::Ready(())
}
}
}