Skip to main content

osiris/sched/
rt.rs

1use crate::{
2    error::Result,
3    sched::{
4        ThreadMap,
5        thread::{self},
6    },
7    types::{
8        rbtree::RbTree,
9        traits::{Get, GetMut},
10        view::ViewMut,
11    },
12};
13
14pub struct Scheduler<const N: usize> {
15    edf: RbTree<thread::RtTree, thread::UId>,
16}
17
18pub type ServerView<'a, const N: usize> = ViewMut<'a, thread::UId, thread::RtServer, ThreadMap<N>>;
19
20impl<const N: usize> Scheduler<N> {
21    pub const fn new() -> Self {
22        Self { edf: RbTree::new() }
23    }
24
25    pub fn enqueue(
26        &mut self,
27        uid: thread::UId,
28        now: u64,
29        storage: &mut ServerView<N>,
30    ) -> Result<()> {
31        if let Some(server) = storage.get_mut(uid) {
32            // Threads are only enqueued when they are runnable.
33            if server.budget_left() == 0 && server.deadline() != 0 {
34                server.replenish();
35            } else {
36                server.on_wakeup(now);
37            }
38            self.edf.insert(uid, storage)?;
39        }
40        Ok(())
41    }
42
43    /// This should be called on each do_schedule call, to update the internal scheduler state.
44    /// If this function returns Some(u64) it means the current thread has exhausted its budget and should be throttled until the returned timestamp.
45    pub fn put(&mut self, uid: thread::UId, dt: u64, storage: &mut ServerView<N>) -> Option<u64> {
46        if let Some(server) = storage.get_mut(uid) {
47            server.consume(dt)
48        } else {
49            None
50        }
51    }
52
53    pub fn pick(&mut self, storage: &mut ServerView<N>) -> Option<(thread::UId, u32)> {
54        self.edf
55            .min()
56            .and_then(|id| storage.get(id).map(|s| (id, s.budget_left())))
57    }
58
59    pub fn dequeue(&mut self, uid: thread::UId, storage: &mut ServerView<N>) -> Result<()> {
60        self.edf.remove(uid, storage)
61    }
62}