1#[cfg(feature = "alloc")]
18extern crate alloc;
19#[cfg(feature = "alloc")]
20use alloc::collections::BTreeMap;
21
22use crate::{
23 error::{KernelError, SchedError},
24 process::ProcessId,
25};
26
27pub const SCHED_DEADLINE: u32 = 6;
29
30const UTIL_SCALE: u64 = 1000;
35
36const MAX_DEADLINE_TASKS: usize = 64;
38
39#[derive(Debug, Clone, Copy)]
41pub struct DeadlineEntity {
42 pub pid: ProcessId,
44 pub runtime_ns: u64,
46 pub deadline_ns: u64,
48 pub period_ns: u64,
50 pub runtime_remaining: u64,
53 pub deadline_abs: u64,
56 pub period_start: u64,
59 pub throttled: bool,
61}
62
63impl DeadlineEntity {
64 pub fn new(
73 pid: ProcessId,
74 runtime_ns: u64,
75 deadline_ns: u64,
76 period_ns: u64,
77 now_ns: u64,
78 ) -> Self {
79 Self {
80 pid,
81 runtime_ns,
82 deadline_ns,
83 period_ns,
84 runtime_remaining: runtime_ns,
85 deadline_abs: now_ns.saturating_add(deadline_ns),
86 period_start: now_ns,
87 throttled: false,
88 }
89 }
90
91 fn utilization_permille(&self) -> u64 {
94 if self.period_ns == 0 {
95 return UTIL_SCALE; }
97 self.runtime_ns.saturating_mul(UTIL_SCALE) / self.period_ns
98 }
99
100 fn needs_replenish(&self, now_ns: u64) -> bool {
102 now_ns >= self.period_start.saturating_add(self.period_ns)
103 }
104
105 fn replenish(&mut self, now_ns: u64) {
107 if self.period_ns > 0 {
109 while self.period_start.saturating_add(self.period_ns) <= now_ns {
110 self.period_start = self.period_start.saturating_add(self.period_ns);
111 }
112 }
113 self.deadline_abs = self.period_start.saturating_add(self.deadline_ns);
114 self.runtime_remaining = self.runtime_ns;
115 self.throttled = false;
116 }
117}
118
119#[derive(Debug, Clone, Copy)]
121pub struct SchedAttr {
122 pub policy: u32,
124 pub runtime_ns: u64,
126 pub deadline_ns: u64,
128 pub period_ns: u64,
130}
131
132impl SchedAttr {
133 pub fn validate(&self) -> Result<(), KernelError> {
135 if self.policy != SCHED_DEADLINE {
136 return Err(KernelError::InvalidArgument {
137 name: "policy",
138 value: "must be SCHED_DEADLINE",
139 });
140 }
141 if self.runtime_ns == 0 {
142 return Err(KernelError::InvalidArgument {
143 name: "runtime_ns",
144 value: "must be > 0",
145 });
146 }
147 if self.deadline_ns == 0 {
148 return Err(KernelError::InvalidArgument {
149 name: "deadline_ns",
150 value: "must be > 0",
151 });
152 }
153 if self.period_ns == 0 {
154 return Err(KernelError::InvalidArgument {
155 name: "period_ns",
156 value: "must be > 0",
157 });
158 }
159 if self.runtime_ns > self.deadline_ns {
160 return Err(KernelError::InvalidArgument {
161 name: "runtime_ns",
162 value: "must be <= deadline_ns",
163 });
164 }
165 if self.deadline_ns > self.period_ns {
166 return Err(KernelError::InvalidArgument {
167 name: "deadline_ns",
168 value: "must be <= period_ns",
169 });
170 }
171 Ok(())
172 }
173}
174
175#[cfg(feature = "alloc")]
181pub struct DeadlineScheduler {
182 tasks: BTreeMap<u64, DeadlineEntity>,
184 total_utilization: u64,
187 current_pid: Option<u64>,
189}
190
191#[cfg(feature = "alloc")]
192impl Default for DeadlineScheduler {
193 fn default() -> Self {
194 Self::new()
195 }
196}
197
198#[cfg(feature = "alloc")]
199impl DeadlineScheduler {
200 pub const fn new() -> Self {
202 Self {
203 tasks: BTreeMap::new(),
204 total_utilization: 0,
205 current_pid: None,
206 }
207 }
208
209 pub fn task_count(&self) -> usize {
211 self.tasks.len()
212 }
213
214 pub fn total_utilization(&self) -> u64 {
216 self.total_utilization
217 }
218
219 pub fn has_task(&self, pid: ProcessId) -> bool {
221 self.tasks.contains_key(&pid.0)
222 }
223
224 pub fn add_task(
243 &mut self,
244 pid: ProcessId,
245 runtime_ns: u64,
246 deadline_ns: u64,
247 period_ns: u64,
248 now_ns: u64,
249 ) -> Result<(), KernelError> {
250 let attr = SchedAttr {
252 policy: SCHED_DEADLINE,
253 runtime_ns,
254 deadline_ns,
255 period_ns,
256 };
257 attr.validate()?;
258
259 if self.tasks.contains_key(&pid.0) {
261 return Err(KernelError::SchedulerError(SchedError::AlreadyScheduled));
262 }
263
264 if self.tasks.len() >= MAX_DEADLINE_TASKS {
266 return Err(KernelError::ResourceExhausted {
267 resource: "deadline_tasks",
268 });
269 }
270
271 let entity = DeadlineEntity::new(pid, runtime_ns, deadline_ns, period_ns, now_ns);
273 let new_util = entity.utilization_permille();
274 let proposed_total = self.total_utilization.saturating_add(new_util);
275
276 if proposed_total > UTIL_SCALE {
277 return Err(KernelError::ResourceExhausted {
278 resource: "deadline_bandwidth",
279 });
280 }
281
282 self.total_utilization = proposed_total;
283 self.tasks.insert(pid.0, entity);
284 Ok(())
285 }
286
287 pub fn add_task_attr(
289 &mut self,
290 pid: ProcessId,
291 attr: &SchedAttr,
292 now_ns: u64,
293 ) -> Result<(), KernelError> {
294 attr.validate()?;
295 self.add_task(
296 pid,
297 attr.runtime_ns,
298 attr.deadline_ns,
299 attr.period_ns,
300 now_ns,
301 )
302 }
303
304 pub fn remove_task(&mut self, pid: ProcessId) -> Result<DeadlineEntity, KernelError> {
308 let entity = self
309 .tasks
310 .remove(&pid.0)
311 .ok_or(KernelError::SchedulerError(SchedError::TaskNotFound {
312 id: pid.0,
313 }))?;
314
315 let util = entity.utilization_permille();
317 self.total_utilization = self.total_utilization.saturating_sub(util);
318
319 if self.current_pid == Some(pid.0) {
321 self.current_pid = None;
322 }
323
324 Ok(entity)
325 }
326
327 pub fn pick_next(&mut self) -> Option<ProcessId> {
333 let mut best_pid: Option<u64> = None;
334 let mut best_deadline = u64::MAX;
335
336 for (&pid, entity) in self.tasks.iter() {
337 if entity.throttled {
339 continue;
340 }
341 if entity.runtime_remaining == 0 {
343 continue;
344 }
345 if entity.deadline_abs < best_deadline {
347 best_deadline = entity.deadline_abs;
348 best_pid = Some(pid);
349 }
350 }
351
352 if let Some(pid) = best_pid {
353 self.current_pid = Some(pid);
354 Some(ProcessId(pid))
355 } else {
356 self.current_pid = None;
357 None
358 }
359 }
360
361 pub fn tick(&mut self, elapsed_ns: u64) -> bool {
373 let pid = match self.current_pid {
374 Some(pid) => pid,
375 None => return false,
376 };
377
378 let entity = match self.tasks.get_mut(&pid) {
379 Some(e) => e,
380 None => {
381 self.current_pid = None;
382 return false;
383 }
384 };
385
386 if entity.runtime_remaining <= elapsed_ns {
387 entity.runtime_remaining = 0;
388 entity.throttled = true;
389 true } else {
391 entity.runtime_remaining = entity.runtime_remaining.saturating_sub(elapsed_ns);
392 false
393 }
394 }
395
396 pub fn replenish(&mut self, now_ns: u64) -> usize {
407 let mut count = 0;
408
409 for entity in self.tasks.values_mut() {
410 if entity.needs_replenish(now_ns) {
411 entity.replenish(now_ns);
412 count += 1;
413 }
414 }
415
416 count
417 }
418
419 pub fn next_deadline_event(&self, now_ns: u64) -> Option<u64> {
429 if self.tasks.is_empty() {
430 return None;
431 }
432
433 let mut min_event = u64::MAX;
434
435 for entity in self.tasks.values() {
436 let period_end = entity.period_start.saturating_add(entity.period_ns);
438 if period_end > now_ns {
439 let until_replenish = period_end.saturating_sub(now_ns);
440 if until_replenish < min_event {
441 min_event = until_replenish;
442 }
443 }
444
445 if entity.deadline_abs > now_ns {
447 let until_deadline = entity.deadline_abs.saturating_sub(now_ns);
448 if until_deadline < min_event {
449 min_event = until_deadline;
450 }
451 }
452 }
453
454 if let Some(pid) = self.current_pid {
456 if let Some(entity) = self.tasks.get(&pid) {
457 if !entity.throttled && entity.runtime_remaining < min_event {
458 min_event = entity.runtime_remaining;
459 }
460 }
461 }
462
463 if min_event == u64::MAX {
464 None
465 } else {
466 Some(min_event)
467 }
468 }
469
470 pub fn should_preempt_cfs(&self) -> bool {
475 self.tasks
476 .values()
477 .any(|e| !e.throttled && e.runtime_remaining > 0)
478 }
479
480 pub fn get_task(&self, pid: ProcessId) -> Option<&DeadlineEntity> {
482 self.tasks.get(&pid.0)
483 }
484
485 pub fn set_current(&mut self, pid: Option<ProcessId>) {
487 self.current_pid = pid.map(|p| p.0);
488 }
489
490 pub fn current(&self) -> Option<ProcessId> {
492 self.current_pid.map(ProcessId)
493 }
494
495 fn recalculate_utilization(&mut self) {
498 self.total_utilization = self
499 .tasks
500 .values()
501 .map(|e| e.utilization_permille())
502 .fold(0u64, |acc, u| acc.saturating_add(u));
503 }
504}
505
506#[cfg(feature = "alloc")]
508pub(crate) static DEADLINE_SCHEDULER: spin::Mutex<DeadlineScheduler> =
509 spin::Mutex::new(DeadlineScheduler::new());
510
511#[cfg(test)]
516mod tests {
517 #[cfg(feature = "alloc")]
518 #[allow(unused_imports)]
519 use alloc::vec;
520
521 use super::*;
522
523 #[cfg(feature = "alloc")]
525 fn make_scheduler() -> DeadlineScheduler {
526 DeadlineScheduler::new()
527 }
528
529 #[test]
532 fn test_entity_utilization_permille() {
533 let e = DeadlineEntity::new(ProcessId(1), 5_000_000, 10_000_000, 10_000_000, 0);
535 assert_eq!(e.utilization_permille(), 500);
536
537 let e = DeadlineEntity::new(ProcessId(2), 10_000_000, 10_000_000, 10_000_000, 0);
539 assert_eq!(e.utilization_permille(), 1000);
540
541 let e = DeadlineEntity::new(ProcessId(3), 1_000_000, 10_000_000, 10_000_000, 0);
543 assert_eq!(e.utilization_permille(), 100);
544
545 let e = DeadlineEntity::new(ProcessId(4), 1_000_000, 0, 0, 0);
547 assert_eq!(e.utilization_permille(), 1000);
548 }
549
550 #[test]
551 fn test_entity_needs_replenish() {
552 let e = DeadlineEntity::new(ProcessId(1), 1_000_000, 5_000_000, 10_000_000, 100);
553 assert!(!e.needs_replenish(5_000_000));
555 assert!(e.needs_replenish(10_000_100));
557 assert!(e.needs_replenish(20_000_000));
559 }
560
561 #[test]
562 fn test_entity_replenish() {
563 let mut e = DeadlineEntity::new(ProcessId(1), 2_000_000, 5_000_000, 10_000_000, 0);
564 e.runtime_remaining = 0;
565 e.throttled = true;
566
567 e.replenish(10_000_000);
569 assert_eq!(e.runtime_remaining, 2_000_000);
570 assert_eq!(e.period_start, 10_000_000);
571 assert_eq!(e.deadline_abs, 15_000_000); assert!(!e.throttled);
573 }
574
575 #[test]
576 fn test_entity_replenish_skipped_periods() {
577 let mut e = DeadlineEntity::new(ProcessId(1), 1_000_000, 5_000_000, 10_000_000, 0);
578 e.runtime_remaining = 0;
579 e.throttled = true;
580
581 e.replenish(35_000_000);
583 assert_eq!(e.period_start, 30_000_000);
584 assert_eq!(e.deadline_abs, 35_000_000);
585 assert_eq!(e.runtime_remaining, 1_000_000);
586 }
587
588 #[test]
591 fn test_sched_attr_validate_valid() {
592 let attr = SchedAttr {
593 policy: SCHED_DEADLINE,
594 runtime_ns: 1_000_000,
595 deadline_ns: 5_000_000,
596 period_ns: 10_000_000,
597 };
598 assert!(attr.validate().is_ok());
599 }
600
601 #[test]
602 fn test_sched_attr_validate_wrong_policy() {
603 let attr = SchedAttr {
604 policy: 0,
605 runtime_ns: 1_000_000,
606 deadline_ns: 5_000_000,
607 period_ns: 10_000_000,
608 };
609 assert!(attr.validate().is_err());
610 }
611
612 #[test]
613 fn test_sched_attr_validate_zero_runtime() {
614 let attr = SchedAttr {
615 policy: SCHED_DEADLINE,
616 runtime_ns: 0,
617 deadline_ns: 5_000_000,
618 period_ns: 10_000_000,
619 };
620 assert!(attr.validate().is_err());
621 }
622
623 #[test]
624 fn test_sched_attr_validate_runtime_exceeds_deadline() {
625 let attr = SchedAttr {
626 policy: SCHED_DEADLINE,
627 runtime_ns: 6_000_000,
628 deadline_ns: 5_000_000,
629 period_ns: 10_000_000,
630 };
631 assert!(attr.validate().is_err());
632 }
633
634 #[test]
635 fn test_sched_attr_validate_deadline_exceeds_period() {
636 let attr = SchedAttr {
637 policy: SCHED_DEADLINE,
638 runtime_ns: 1_000_000,
639 deadline_ns: 15_000_000,
640 period_ns: 10_000_000,
641 };
642 assert!(attr.validate().is_err());
643 }
644
645 #[cfg(feature = "alloc")]
648 #[test]
649 fn test_add_task_basic() {
650 let mut sched = make_scheduler();
651 let result = sched.add_task(
652 ProcessId(1),
653 1_000_000, 5_000_000, 10_000_000, 0,
657 );
658 assert!(result.is_ok());
659 assert_eq!(sched.task_count(), 1);
660 assert_eq!(sched.total_utilization(), 100); }
662
663 #[cfg(feature = "alloc")]
664 #[test]
665 fn test_add_task_duplicate() {
666 let mut sched = make_scheduler();
667 let _ = sched.add_task(ProcessId(1), 1_000_000, 5_000_000, 10_000_000, 0);
668 let result = sched.add_task(ProcessId(1), 1_000_000, 5_000_000, 10_000_000, 0);
669 assert!(result.is_err());
670 }
671
672 #[cfg(feature = "alloc")]
673 #[test]
674 fn test_admission_control_accept() {
675 let mut sched = make_scheduler();
676 assert!(sched
678 .add_task(ProcessId(1), 3_000_000, 10_000_000, 10_000_000, 0)
679 .is_ok());
680 assert!(sched
681 .add_task(ProcessId(2), 3_000_000, 10_000_000, 10_000_000, 0)
682 .is_ok());
683 assert!(sched
684 .add_task(ProcessId(3), 3_000_000, 10_000_000, 10_000_000, 0)
685 .is_ok());
686 assert_eq!(sched.total_utilization(), 900);
687 }
688
689 #[cfg(feature = "alloc")]
690 #[test]
691 fn test_admission_control_reject() {
692 let mut sched = make_scheduler();
693 assert!(sched
695 .add_task(ProcessId(1), 6_000_000, 10_000_000, 10_000_000, 0)
696 .is_ok());
697 let result = sched.add_task(ProcessId(2), 5_000_000, 10_000_000, 10_000_000, 0);
698 assert!(result.is_err());
699 assert_eq!(sched.task_count(), 1);
700 assert_eq!(sched.total_utilization(), 600);
701 }
702
703 #[cfg(feature = "alloc")]
704 #[test]
705 fn test_admission_control_exact_100_percent() {
706 let mut sched = make_scheduler();
707 assert!(sched
709 .add_task(ProcessId(1), 10_000_000, 10_000_000, 10_000_000, 0)
710 .is_ok());
711 assert_eq!(sched.total_utilization(), 1000);
712 let result = sched.add_task(ProcessId(2), 1_000_000, 10_000_000, 10_000_000, 0);
714 assert!(result.is_err());
715 }
716
717 #[cfg(feature = "alloc")]
718 #[test]
719 fn test_remove_task() {
720 let mut sched = make_scheduler();
721 let _ = sched.add_task(ProcessId(1), 3_000_000, 10_000_000, 10_000_000, 0);
722 let _ = sched.add_task(ProcessId(2), 2_000_000, 10_000_000, 10_000_000, 0);
723 assert_eq!(sched.total_utilization(), 500); let result = sched.remove_task(ProcessId(1));
726 assert!(result.is_ok());
727 assert_eq!(sched.task_count(), 1);
728 assert_eq!(sched.total_utilization(), 200); }
730
731 #[cfg(feature = "alloc")]
732 #[test]
733 fn test_remove_task_not_found() {
734 let mut sched = make_scheduler();
735 let result = sched.remove_task(ProcessId(99));
736 assert!(result.is_err());
737 }
738
739 #[cfg(feature = "alloc")]
740 #[test]
741 fn test_remove_then_readmit() {
742 let mut sched = make_scheduler();
743 let _ = sched.add_task(ProcessId(1), 10_000_000, 10_000_000, 10_000_000, 0);
745 assert_eq!(sched.total_utilization(), 1000);
746
747 let _ = sched.remove_task(ProcessId(1));
749 assert_eq!(sched.total_utilization(), 0);
750
751 assert!(sched
753 .add_task(ProcessId(2), 5_000_000, 10_000_000, 10_000_000, 0)
754 .is_ok());
755 }
756
757 #[cfg(feature = "alloc")]
758 #[test]
759 fn test_pick_next_earliest_deadline() {
760 let mut sched = make_scheduler();
761 let _ = sched.add_task(ProcessId(1), 1_000_000, 20_000_000, 30_000_000, 0);
763 let _ = sched.add_task(ProcessId(2), 1_000_000, 10_000_000, 30_000_000, 0);
765 let _ = sched.add_task(ProcessId(3), 1_000_000, 15_000_000, 30_000_000, 0);
767
768 let next = sched.pick_next();
769 assert_eq!(next, Some(ProcessId(2))); }
771
772 #[cfg(feature = "alloc")]
773 #[test]
774 fn test_pick_next_skips_throttled() {
775 let mut sched = make_scheduler();
776 let _ = sched.add_task(ProcessId(1), 1_000_000, 5_000_000, 10_000_000, 0);
778 let _ = sched.add_task(ProcessId(2), 1_000_000, 8_000_000, 10_000_000, 0);
780
781 if let Some(e) = sched.tasks.get_mut(&1) {
783 e.throttled = true;
784 e.runtime_remaining = 0;
785 }
786
787 let next = sched.pick_next();
788 assert_eq!(next, Some(ProcessId(2)));
789 }
790
791 #[cfg(feature = "alloc")]
792 #[test]
793 fn test_pick_next_none_when_all_throttled() {
794 let mut sched = make_scheduler();
795 let _ = sched.add_task(ProcessId(1), 1_000_000, 5_000_000, 10_000_000, 0);
796
797 if let Some(e) = sched.tasks.get_mut(&1) {
798 e.throttled = true;
799 e.runtime_remaining = 0;
800 }
801
802 assert_eq!(sched.pick_next(), None);
803 }
804
805 #[cfg(feature = "alloc")]
806 #[test]
807 fn test_pick_next_empty() {
808 let mut sched = make_scheduler();
809 assert_eq!(sched.pick_next(), None);
810 }
811
812 #[cfg(feature = "alloc")]
813 #[test]
814 fn test_tick_decrements_runtime() {
815 let mut sched = make_scheduler();
816 let _ = sched.add_task(ProcessId(1), 5_000_000, 10_000_000, 10_000_000, 0);
817 sched.current_pid = Some(1);
818
819 let throttled = sched.tick(1_000_000); assert!(!throttled);
821
822 let entity = sched.get_task(ProcessId(1)).unwrap();
823 assert_eq!(entity.runtime_remaining, 4_000_000);
824 assert!(!entity.throttled);
825 }
826
827 #[cfg(feature = "alloc")]
828 #[test]
829 fn test_tick_runtime_exhaustion() {
830 let mut sched = make_scheduler();
831 let _ = sched.add_task(ProcessId(1), 2_000_000, 10_000_000, 10_000_000, 0);
832 sched.current_pid = Some(1);
833
834 let throttled = sched.tick(2_000_000);
836 assert!(throttled);
837
838 let entity = sched.get_task(ProcessId(1)).unwrap();
839 assert_eq!(entity.runtime_remaining, 0);
840 assert!(entity.throttled);
841 }
842
843 #[cfg(feature = "alloc")]
844 #[test]
845 fn test_tick_over_exhaustion() {
846 let mut sched = make_scheduler();
847 let _ = sched.add_task(ProcessId(1), 1_000_000, 10_000_000, 10_000_000, 0);
848 sched.current_pid = Some(1);
849
850 let throttled = sched.tick(5_000_000);
852 assert!(throttled);
853
854 let entity = sched.get_task(ProcessId(1)).unwrap();
855 assert_eq!(entity.runtime_remaining, 0);
856 assert!(entity.throttled);
857 }
858
859 #[cfg(feature = "alloc")]
860 #[test]
861 fn test_tick_no_current() {
862 let mut sched = make_scheduler();
863 let throttled = sched.tick(1_000_000);
864 assert!(!throttled);
865 }
866
867 #[cfg(feature = "alloc")]
868 #[test]
869 fn test_replenish_at_period_boundary() {
870 let mut sched = make_scheduler();
871 let _ = sched.add_task(ProcessId(1), 2_000_000, 5_000_000, 10_000_000, 0);
872 sched.current_pid = Some(1);
873
874 sched.tick(2_000_000);
876 assert!(sched.get_task(ProcessId(1)).unwrap().throttled);
877
878 let count = sched.replenish(10_000_000);
880 assert_eq!(count, 1);
881
882 let entity = sched.get_task(ProcessId(1)).unwrap();
883 assert_eq!(entity.runtime_remaining, 2_000_000);
884 assert!(!entity.throttled);
885 assert_eq!(entity.period_start, 10_000_000);
886 }
887
888 #[cfg(feature = "alloc")]
889 #[test]
890 fn test_replenish_not_yet_due() {
891 let mut sched = make_scheduler();
892 let _ = sched.add_task(ProcessId(1), 2_000_000, 5_000_000, 10_000_000, 0);
893
894 let count = sched.replenish(5_000_000);
896 assert_eq!(count, 0);
897 }
898
899 #[cfg(feature = "alloc")]
900 #[test]
901 fn test_next_deadline_event() {
902 let mut sched = make_scheduler();
903 let _ = sched.add_task(ProcessId(1), 2_000_000, 5_000_000, 10_000_000, 0);
905
906 let event = sched.next_deadline_event(0);
907 assert!(event.is_some());
908 assert_eq!(event.unwrap(), 5_000_000);
911 }
912
913 #[cfg(feature = "alloc")]
914 #[test]
915 fn test_next_deadline_event_with_current_runtime() {
916 let mut sched = make_scheduler();
917 let _ = sched.add_task(ProcessId(1), 1_000_000, 5_000_000, 10_000_000, 0);
919 sched.current_pid = Some(1);
920
921 let event = sched.next_deadline_event(0);
922 assert!(event.is_some());
923 assert_eq!(event.unwrap(), 1_000_000);
925 }
926
927 #[cfg(feature = "alloc")]
928 #[test]
929 fn test_next_deadline_event_empty() {
930 let sched = make_scheduler();
931 assert_eq!(sched.next_deadline_event(0), None);
932 }
933
934 #[cfg(feature = "alloc")]
935 #[test]
936 fn test_should_preempt_cfs() {
937 let mut sched = make_scheduler();
938 assert!(!sched.should_preempt_cfs());
939
940 let _ = sched.add_task(ProcessId(1), 1_000_000, 5_000_000, 10_000_000, 0);
941 assert!(sched.should_preempt_cfs());
942
943 if let Some(e) = sched.tasks.get_mut(&1) {
945 e.throttled = true;
946 }
947 assert!(!sched.should_preempt_cfs());
948 }
949
950 #[cfg(feature = "alloc")]
951 #[test]
952 fn test_full_lifecycle() {
953 let mut sched = make_scheduler();
954
955 let _ = sched.add_task(ProcessId(1), 2_000_000, 5_000_000, 10_000_000, 0);
957 let _ = sched.add_task(ProcessId(2), 3_000_000, 8_000_000, 10_000_000, 0);
958 assert_eq!(sched.total_utilization(), 500); assert_eq!(sched.pick_next(), Some(ProcessId(1)));
962
963 assert!(sched.tick(2_000_000));
965
966 assert_eq!(sched.pick_next(), Some(ProcessId(2)));
968
969 assert!(sched.tick(3_000_000));
971
972 assert_eq!(sched.pick_next(), None);
974 assert!(!sched.should_preempt_cfs());
975
976 let count = sched.replenish(10_000_000);
978 assert_eq!(count, 2);
979 assert!(sched.should_preempt_cfs());
980
981 let next = sched.pick_next();
983 assert!(next.is_some());
984 }
985
986 #[cfg(feature = "alloc")]
987 #[test]
988 fn test_recalculate_utilization() {
989 let mut sched = make_scheduler();
990 let _ = sched.add_task(ProcessId(1), 3_000_000, 10_000_000, 10_000_000, 0);
991 let _ = sched.add_task(ProcessId(2), 2_000_000, 10_000_000, 10_000_000, 0);
992
993 sched.total_utilization = 999;
995 sched.recalculate_utilization();
996 assert_eq!(sched.total_utilization, 500); }
998}