1#[derive(Debug, Clone, Copy, PartialEq)]
13#[repr(C)]
14pub struct SchedulerScore {
15 pub score: f32,
18
19 pub deadline_urgency: f32,
21
22 pub novelty_boost: f32,
24
25 pub risk_penalty: f32,
27}
28
29impl SchedulerScore {
30 #[inline]
32 #[must_use]
33 pub const fn new(
34 deadline_urgency: f32,
35 novelty_boost: f32,
36 risk_penalty: f32,
37 ) -> Self {
38 Self {
39 score: deadline_urgency + novelty_boost - risk_penalty,
40 deadline_urgency,
41 novelty_boost,
42 risk_penalty,
43 }
44 }
45
46 #[inline]
48 #[must_use]
49 pub const fn normal() -> Self {
50 Self {
51 score: 1.0,
52 deadline_urgency: 1.0,
53 novelty_boost: 0.0,
54 risk_penalty: 0.0,
55 }
56 }
57
58 #[inline]
60 #[must_use]
61 pub const fn high() -> Self {
62 Self {
63 score: 3.0,
64 deadline_urgency: 3.0,
65 novelty_boost: 0.0,
66 risk_penalty: 0.0,
67 }
68 }
69
70 #[inline]
72 #[must_use]
73 pub const fn critical() -> Self {
74 Self {
75 score: 5.0,
76 deadline_urgency: 5.0,
77 novelty_boost: 0.0,
78 risk_penalty: 0.0,
79 }
80 }
81
82 #[inline]
84 #[must_use]
85 pub fn is_higher_than(&self, other: &Self) -> bool {
86 self.score > other.score
87 }
88
89 #[inline]
91 #[must_use]
92 pub fn with_novelty(mut self, boost: f32) -> Self {
93 let clamped = if boost < 0.0 {
94 0.0
95 } else if boost > 1.0 {
96 1.0
97 } else {
98 boost
99 };
100 self.novelty_boost = clamped;
101 self.score = self.deadline_urgency + self.novelty_boost - self.risk_penalty;
102 self
103 }
104
105 #[inline]
107 #[must_use]
108 pub fn with_risk(mut self, penalty: f32) -> Self {
109 let clamped = if penalty < 0.0 {
110 0.0
111 } else if penalty > 2.0 {
112 2.0
113 } else {
114 penalty
115 };
116 self.risk_penalty = clamped;
117 self.score = self.deadline_urgency + self.novelty_boost - self.risk_penalty;
118 self
119 }
120}
121
122impl Default for SchedulerScore {
123 fn default() -> Self {
124 Self::normal()
125 }
126}
127
128impl PartialOrd for SchedulerScore {
129 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
130 self.score.partial_cmp(&other.score)
131 }
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140#[repr(C)]
141pub struct SchedulerPartition {
142 pub partition_id: u32,
144
145 pub time_slice_us: u32,
147
148 pub task_count: u32,
150
151 pub remaining_us: u32,
153}
154
155impl SchedulerPartition {
156 #[inline]
158 #[must_use]
159 pub const fn new(partition_id: u32, time_slice_us: u32) -> Self {
160 Self {
161 partition_id,
162 time_slice_us,
163 task_count: 0,
164 remaining_us: time_slice_us,
165 }
166 }
167
168 #[inline]
170 #[must_use]
171 pub const fn is_exhausted(&self) -> bool {
172 self.remaining_us == 0
173 }
174
175 #[inline]
177 pub fn reset(&mut self) {
178 self.remaining_us = self.time_slice_us;
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn test_scheduler_score_ordering() {
188 let low = SchedulerScore::normal();
189 let high = SchedulerScore::high();
190 let critical = SchedulerScore::critical();
191
192 assert!(high.is_higher_than(&low));
193 assert!(critical.is_higher_than(&high));
194 }
195
196 #[test]
197 fn test_scheduler_score_with_novelty() {
198 let base = SchedulerScore::normal();
199 let boosted = base.with_novelty(0.5);
200
201 assert!(boosted.is_higher_than(&base));
202 assert!((boosted.novelty_boost - 0.5).abs() < 0.001);
203 }
204
205 #[test]
206 fn test_scheduler_score_with_risk() {
207 let base = SchedulerScore::normal();
208 let penalized = base.with_risk(1.0);
209
210 assert!(!penalized.is_higher_than(&base));
211 assert!((penalized.risk_penalty - 1.0).abs() < 0.001);
212 }
213
214 #[test]
215 fn test_scheduler_partition() {
216 let mut partition = SchedulerPartition::new(1, 10000);
217 assert!(!partition.is_exhausted());
218
219 partition.remaining_us = 0;
220 assert!(partition.is_exhausted());
221
222 partition.reset();
223 assert!(!partition.is_exhausted());
224 }
225}