zerodds_corba_rt/
scheduling.rs1use alloc::vec::Vec;
20
21use crate::priority::Priority;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub struct Task {
26 pub period: u64,
28 pub wcet: u64,
30 pub deadline: u64,
32 pub priority: Priority,
35}
36
37impl Task {
38 #[must_use]
40 pub fn new(period: u64, wcet: u64, priority: Priority) -> Self {
41 Self {
42 period,
43 wcet,
44 deadline: 0,
45 priority,
46 }
47 }
48
49 #[must_use]
51 pub fn with_deadline(period: u64, wcet: u64, deadline: u64, priority: Priority) -> Self {
52 Self {
53 period,
54 wcet,
55 deadline,
56 priority,
57 }
58 }
59
60 #[must_use]
62 pub fn effective_deadline(&self) -> u64 {
63 if self.deadline == 0 {
64 self.period
65 } else {
66 self.deadline
67 }
68 }
69}
70
71#[derive(Debug, Clone, Default)]
73pub struct TaskSet {
74 pub tasks: Vec<Task>,
76}
77
78#[must_use]
81fn nth_root_of_two(n: u32) -> f64 {
82 if n == 0 {
83 return 1.0;
84 }
85 if n == 1 {
86 return 2.0;
87 }
88 let nf = f64::from(n);
89 let mut x = 1.0_f64;
90 let mut iter = 0;
92 while iter < 100 {
93 let mut x_nm1 = 1.0_f64; let mut k = 0;
95 while k < n - 1 {
96 x_nm1 *= x;
97 k += 1;
98 }
99 let x_n = x_nm1 * x;
100 let denom = nf * x_nm1;
101 if denom == 0.0 {
102 break;
103 }
104 let next = x - (x_n - 2.0) / denom;
105 let delta = if next > x { next - x } else { x - next };
106 x = next;
107 if delta < 1e-13 {
108 break;
109 }
110 iter += 1;
111 }
112 x
113}
114
115impl TaskSet {
116 #[must_use]
118 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn push(&mut self, task: Task) {
124 self.tasks.push(task);
125 }
126
127 #[must_use]
129 pub fn len(&self) -> usize {
130 self.tasks.len()
131 }
132
133 #[must_use]
135 pub fn is_empty(&self) -> bool {
136 self.tasks.is_empty()
137 }
138
139 #[must_use]
141 pub fn utilization(&self) -> f64 {
142 self.tasks
143 .iter()
144 .filter(|t| t.period > 0)
145 .map(|t| t.wcet as f64 / t.period as f64)
146 .sum()
147 }
148
149 #[must_use]
151 pub fn liu_layland_bound(n: usize) -> f64 {
152 if n == 0 {
153 return 1.0;
154 }
155 let nf = n as f64;
156 nf * (nth_root_of_two(n as u32) - 1.0)
157 }
158
159 pub fn assign_rate_monotonic(&mut self) {
163 let n = self.tasks.len();
164 if n == 0 {
165 return;
166 }
167 let mut order: Vec<usize> = (0..n).collect();
169 order.sort_by_key(|&i| self.tasks[i].period);
170 for (rank, &idx) in order.iter().enumerate() {
172 let prio_val = (n - 1 - rank) as i32;
173 self.tasks[idx].priority = Priority::clamped(prio_val);
174 }
175 }
176
177 fn higher_priority_than(&self, i: usize) -> impl Iterator<Item = &Task> {
180 let pi = self.tasks[i].priority;
181 self.tasks
182 .iter()
183 .enumerate()
184 .filter(move |(j, t)| *j != i && t.priority > pi)
185 .map(|(_, t)| t)
186 }
187
188 #[must_use]
191 pub fn response_time(&self, i: usize) -> Option<u64> {
192 if i >= self.tasks.len() {
193 return None;
194 }
195 let ti = self.tasks[i];
196 let deadline = ti.effective_deadline();
197 let mut r = ti.wcet;
198 loop {
199 let interference: u64 = self
200 .higher_priority_than(i)
201 .map(|t| r.div_ceil(t.period) * t.wcet)
202 .sum();
203 let next = ti.wcet + interference;
204 if next == r {
205 return if r <= deadline { Some(r) } else { None };
206 }
207 if next > deadline {
208 return None; }
210 r = next;
211 }
212 }
213
214 #[must_use]
216 pub fn response_times(&self) -> Vec<Option<u64>> {
217 (0..self.tasks.len())
218 .map(|i| self.response_time(i))
219 .collect()
220 }
221
222 #[must_use]
224 pub fn is_schedulable_rta(&self) -> bool {
225 (0..self.tasks.len()).all(|i| self.response_time(i).is_some())
226 }
227
228 #[must_use]
232 pub fn is_schedulable_ll(&self) -> bool {
233 self.utilization() <= Self::liu_layland_bound(self.tasks.len()) + 1e-9
234 }
235}
236
237#[cfg(test)]
238#[allow(clippy::unwrap_used, clippy::panic, clippy::float_cmp)]
239mod tests {
240 use super::*;
241
242 fn p(v: i16) -> Priority {
243 Priority::new(v).unwrap()
244 }
245
246 #[test]
247 fn implicit_deadline_defaults_to_period() {
248 let t = Task::new(10, 3, p(5));
249 assert_eq!(t.effective_deadline(), 10);
250 let t2 = Task::with_deadline(10, 3, 7, p(5));
251 assert_eq!(t2.effective_deadline(), 7);
252 }
253
254 #[test]
255 fn rate_monotonic_assigns_shortest_period_highest() {
256 let mut ts = TaskSet::new();
257 ts.push(Task::new(20, 5, p(0)));
258 ts.push(Task::new(4, 1, p(0)));
259 ts.push(Task::new(5, 2, p(0)));
260 ts.assign_rate_monotonic();
261 assert!(ts.tasks[1].priority > ts.tasks[2].priority);
263 assert!(ts.tasks[2].priority > ts.tasks[0].priority);
264 }
265
266 #[test]
267 fn response_time_analysis_exact_values() {
268 let mut ts = TaskSet::new();
270 ts.push(Task::new(4, 1, p(0)));
271 ts.push(Task::new(5, 2, p(0)));
272 ts.push(Task::new(20, 5, p(0)));
273 ts.assign_rate_monotonic();
274 let r = ts.response_times();
275 assert_eq!(r[0], Some(1)); assert_eq!(r[1], Some(3)); assert_eq!(r[2], Some(15)); assert!(ts.is_schedulable_rta());
279 }
280
281 #[test]
282 fn rta_stronger_than_liu_layland() {
283 let mut ts = TaskSet::new();
286 ts.push(Task::new(4, 1, p(0)));
287 ts.push(Task::new(5, 2, p(0)));
288 ts.push(Task::new(20, 5, p(0)));
289 ts.assign_rate_monotonic();
290 assert!((ts.utilization() - 0.9).abs() < 1e-9);
291 assert!(!ts.is_schedulable_ll());
292 assert!(ts.is_schedulable_rta());
293 }
294
295 #[test]
296 fn overloaded_set_is_unschedulable() {
297 let mut ts = TaskSet::new();
299 ts.push(Task::new(4, 3, p(0)));
300 ts.push(Task::new(5, 3, p(0)));
301 ts.assign_rate_monotonic();
302 assert!(!ts.is_schedulable_rta());
303 assert!(ts.response_time(1).is_none());
304 }
305
306 #[test]
307 fn liu_layland_bound_known_points() {
308 assert!((TaskSet::liu_layland_bound(1) - 1.0).abs() < 1e-9);
309 assert!((TaskSet::liu_layland_bound(2) - 0.8284271).abs() < 1e-6);
310 assert!((TaskSet::liu_layland_bound(3) - 0.7797631).abs() < 1e-6);
311 let big = TaskSet::liu_layland_bound(1000);
313 assert!((big - core::f64::consts::LN_2).abs() < 1e-3, "big={big}");
314 }
315
316 #[test]
317 fn constrained_deadline_tightens_test() {
318 let mut ts = TaskSet::new();
321 ts.push(Task::new(4, 1, p(0)));
322 ts.push(Task::new(5, 2, p(0)));
323 ts.push(Task::with_deadline(20, 5, 12, p(0)));
324 ts.assign_rate_monotonic();
325 assert!(!ts.is_schedulable_rta());
326 }
327}