strat9_kernel/process/sched_classes/
real_time.rs1use super::{CurrentRuntime, SchedClassRq};
4use crate::{arch::timer::TIMER_HZ, process::task::Task};
5use alloc::sync::Arc;
6use core::sync::atomic::Ordering;
7use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink};
8
9const RT_RR_QUANTUM_TICKS: u64 = TIMER_HZ / 10;
14
15const RT_BUDGET_TICKS: u64 = 100;
23
24const RT_BUDGET_PERIOD_TICKS: u64 = 500;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
32pub struct RealTimePriority(u8);
33
34impl RealTimePriority {
35 pub const MIN: Self = Self(0);
36 pub const MAX: Self = Self(99);
37
38 pub fn new(prio: u8) -> Self {
40 Self(prio.clamp(Self::MIN.0, Self::MAX.0))
41 }
42
43 pub fn get(self) -> u8 {
45 self.0
46 }
47}
48
49intrusive_adapter!(pub RtTaskAdapter = Arc<Task>: Task { rt_link: LinkedListLink });
53
54struct RtPrioQueue {
56 list: LinkedList<RtTaskAdapter>,
57 len: usize,
58}
59
60impl RtPrioQueue {
61 fn new() -> Self {
62 Self {
63 list: LinkedList::new(RtTaskAdapter::new()),
64 len: 0,
65 }
66 }
67
68 fn push_back(&mut self, task: Arc<Task>) {
69 self.list.push_back(task);
70 self.len += 1;
71 }
72
73 fn pop_front(&mut self) -> Option<Arc<Task>> {
74 let task = self.list.pop_front()?;
75 self.len -= 1;
76 Some(task)
77 }
78
79 fn is_empty(&self) -> bool {
80 self.len == 0
81 }
82
83 fn remove_by_id(&mut self, task_id: crate::process::TaskId) -> bool {
85 let mut cursor = self.list.front_mut();
86 loop {
87 match cursor.get() {
88 None => return false,
89 Some(task) if task.id == task_id => {
90 let _ = cursor.remove();
91 self.len -= 1;
92 return true;
93 }
94 Some(_) => cursor.move_next(),
95 }
96 }
97 }
98}
99
100pub struct RealTimeClassRq {
101 queues: [RtPrioQueue; 100],
102 bitmap: u128,
103}
104
105impl RealTimeClassRq {
106 pub fn new() -> Self {
107 Self {
108 queues: core::array::from_fn(|_| RtPrioQueue::new()),
109 bitmap: 0,
110 }
111 }
112
113 fn set_bit(&mut self, prio: u8) {
114 self.bitmap |= 1u128 << prio;
115 }
116
117 fn clear_bit(&mut self, prio: u8) {
118 self.bitmap &= !(1u128 << prio);
119 }
120
121 fn is_budget_exhausted(task: &Task, now: u64) -> bool {
124 if !task.rt_degraded.load(Ordering::Relaxed) {
125 return false;
126 }
127 let period_start = task.rt_budget_period_start.load(Ordering::Relaxed);
128 now.saturating_sub(period_start) < RT_BUDGET_PERIOD_TICKS
129 }
130
131 fn reset_budget(task: &Task, now: u64) {
134 task.rt_budget_remaining
135 .store(RT_BUDGET_TICKS, Ordering::Relaxed);
136 task.rt_budget_period_start.store(now, Ordering::Relaxed);
137 task.rt_degraded.store(false, Ordering::Relaxed);
138 }
139
140 fn front_is_degraded(q: &RtPrioQueue, now: u64) -> bool {
142 if let Some(task) = q.list.front().get() {
143 Self::is_budget_exhausted(task, now)
144 } else {
145 false
146 }
147 }
148}
149
150impl SchedClassRq for RealTimeClassRq {
151 fn enqueue(&mut self, task: Arc<Task>) {
152 let prio = match task.sched_policy() {
153 super::SchedPolicy::RealTimeRR { prio } => prio.get(),
154 super::SchedPolicy::RealTimeFifo { prio } => prio.get(),
155 _ => return,
156 };
157
158 let now = crate::process::scheduler::ticks();
160 if task.rt_degraded.load(Ordering::Relaxed) {
161 let period_start = task.rt_budget_period_start.load(Ordering::Relaxed);
162 if now.saturating_sub(period_start) >= RT_BUDGET_PERIOD_TICKS {
163 Self::reset_budget(&task, now);
164 }
165 } else if task.rt_budget_remaining.load(Ordering::Relaxed) == 0 {
166 Self::reset_budget(&task, now);
168 }
169
170 self.queues[prio as usize].push_back(task);
171 self.set_bit(prio);
172 }
173
174 fn len(&self) -> usize {
175 self.queues.iter().map(|q| q.len).sum()
176 }
177
178 fn pick_next(&mut self) -> Option<Arc<Task>> {
179 if self.bitmap == 0 {
180 return None;
181 }
182
183 let now = crate::process::scheduler::ticks();
184
185 let mut scan_bitmap = self.bitmap;
187 while scan_bitmap != 0 {
188 let prio = 127 - scan_bitmap.leading_zeros() as u8;
189 let q = &mut self.queues[prio as usize];
190
191 if Self::front_is_degraded(q, now) {
192 if let Some(task) = q.pop_front() {
194 q.push_back(task);
195 }
196 if q.is_empty() {
197 self.clear_bit(prio);
198 }
199 scan_bitmap &= !(1u128 << prio);
200 continue;
201 }
202
203 let task = q.pop_front()?;
204 if q.is_empty() {
205 self.clear_bit(prio);
206 }
207 return Some(task);
208 }
209
210 None
212 }
213
214 fn update_current(&mut self, rt: &CurrentRuntime, task: &Task, is_yield: bool) -> bool {
215 if is_yield {
216 return true;
217 }
218
219 let remaining = task.rt_budget_remaining.load(Ordering::Relaxed);
221 let consumed = rt.delta_ticks.min(remaining);
222 let new_remaining = remaining.saturating_sub(consumed);
223 task.rt_budget_remaining
224 .store(new_remaining, Ordering::Relaxed);
225
226 if new_remaining == 0 && consumed > 0 {
229 task.rt_degraded.store(true, Ordering::Relaxed);
230 return true; }
232
233 match task.sched_policy() {
234 super::SchedPolicy::RealTimeRR { .. } => {
235 rt.period_delta_ticks >= RT_RR_QUANTUM_TICKS
237 }
238 super::SchedPolicy::RealTimeFifo { .. } => {
239 false
241 }
242 _ => false,
243 }
244 }
245
246 fn remove(&mut self, task_id: crate::process::TaskId) -> bool {
247 let mut bits = self.bitmap;
248 while bits != 0 {
249 let i = bits.trailing_zeros() as usize;
250 if self.queues[i].remove_by_id(task_id) {
251 if self.queues[i].is_empty() {
252 self.clear_bit(i as u8);
253 }
254 return true;
255 }
256 bits &= !(1u128 << i);
257 }
258 false
259 }
260}