1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Provides APIs for tasks to sleep for specified time durations.
//!
//! Key functions:
//! * The [`sleep`] function delays the current task for a given number of ticks.
//! * The [`sleep_until`] function delays the current task until a specific moment in the future.
//! * The [`sleep_periodic`] function allows for tasks to be delayed for periodic intervals
//!  of time and can be used to implement a period task.
//!
//! TODO: use regular time-keeping abstractions like Duration and Instant.

#![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;

/// Contains the `TaskRef` and the associated wakeup time for an entry in DELAYED_TASKLIST.
#[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
    }
}

// The priority queue depends on `Ord`.
// Explicitly implement the trait so the queue becomes a min-heap
// instead of a max-heap.
impl Ord for SleepingTaskNode {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        // Notice that the we flip the ordering on resume_time.
        other.resume_time.cmp(&self.resume_time)
    }
}

// `PartialOrd` needs to be implemented as well.
impl PartialOrd for SleepingTaskNode {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

lazy_static! {
    /// List of all delayed tasks in the system
    /// Implemented as a min-heap of `SleepingTaskNode` sorted in increasing order of `resume_time`
    static ref DELAYED_TASKLIST: IrqSafeMutex<BinaryHeap<SleepingTaskNode>> 
        = IrqSafeMutex::new(BinaryHeap::new());
}

/// Keeps track of the next task that needs to unblock, by default, it is the maximum time
static NEXT_DELAYED_TASK_UNBLOCK_TIME: AtomicCell<Instant> = AtomicCell::new(Instant::MAX);

/// Helper function adds the id associated with a TaskRef to the list of delayed task.
/// If the resume time is less than the current earliest resume time, then update it.
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);
    }
}

/// Remove the next task from the delayed task list and unblock that task
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),
        }
    }
}

/// Remove all tasks that have been delayed but are able to be unblocked now.
pub fn unblock_sleeping_tasks() {
    let time = now::<Monotonic>();
    while time > NEXT_DELAYED_TASK_UNBLOCK_TIME.load() {
        remove_next_task_from_delayed_tasklist();
    }
}

/// Blocks the current task by putting it to sleep for `duration` ticks.
///
/// Returns the current task's run state if it can't be blocked.
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 the current task to the delayed tasklist and then block it.
    add_to_delayed_tasklist(SleepingTaskNode{action: Action::Sync(current_task.clone()), resume_time});
    current_task.block()?;
    task::schedule();
    Ok(())
}

/// Blocks the current task by putting it to sleep until a specific tick count is reached,
/// given by `resume_time`.
///
/// Returns the current task's run state if it can't be blocked.
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(())
}

/// Asynchronous sleep methods that operate on wakers.
pub mod future {
    use core::task::Poll;
    use super::*;

    /// Wakes up the waker after the specified duration.
    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,
            }
        );
    }

    /// Wakes up the waker at the specified 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(())
        }
    }
}