kernel/scheduler/
round_robin.rs1use core::cell::Cell;
22use core::num::NonZeroU32;
23
24use crate::collections::list::{List, ListLink, ListNode};
25use crate::platform::chip::Chip;
26use crate::process::Process;
27use crate::process::StoppedExecutingReason;
28use crate::scheduler::{Scheduler, SchedulingDecision};
29
30pub struct RoundRobinProcessNode<'a> {
33 proc: &'static Option<&'static dyn Process>,
34 next: ListLink<'a, RoundRobinProcessNode<'a>>,
35}
36
37impl<'a> RoundRobinProcessNode<'a> {
38 pub fn new(proc: &'static Option<&'static dyn Process>) -> RoundRobinProcessNode<'a> {
39 RoundRobinProcessNode {
40 proc,
41 next: ListLink::empty(),
42 }
43 }
44}
45
46impl<'a> ListNode<'a, RoundRobinProcessNode<'a>> for RoundRobinProcessNode<'a> {
47 fn next(&'a self) -> &'a ListLink<'a, RoundRobinProcessNode<'a>> {
48 &self.next
49 }
50}
51
52pub struct RoundRobinSched<'a> {
54 time_remaining: Cell<u32>,
55 timeslice_length: u32,
56 pub processes: List<'a, RoundRobinProcessNode<'a>>,
57 last_rescheduled: Cell<bool>,
58}
59
60impl<'a> RoundRobinSched<'a> {
61 const DEFAULT_TIMESLICE_US: u32 = 10000;
63 pub const fn new() -> RoundRobinSched<'a> {
64 Self::new_with_time(Self::DEFAULT_TIMESLICE_US)
65 }
66
67 pub const fn new_with_time(time_us: u32) -> RoundRobinSched<'a> {
68 RoundRobinSched {
69 time_remaining: Cell::new(time_us),
70 timeslice_length: time_us,
71 processes: List::new(),
72 last_rescheduled: Cell::new(false),
73 }
74 }
75}
76
77impl<C: Chip> Scheduler<C> for RoundRobinSched<'_> {
78 fn next(&self) -> SchedulingDecision {
79 let mut first_head = None;
80 let mut next = None;
81
82 while let Some(node) = self.processes.head() {
85 match first_head {
87 None => first_head = Some(node),
88 Some(first_head) => {
89 if core::ptr::eq(first_head, node) {
91 return SchedulingDecision::TrySleep;
92 }
93 }
94 }
95 match node.proc {
96 Some(proc) => {
97 if proc.ready() {
98 next = Some(proc.processid());
99 break;
100 }
101 self.processes.push_tail(self.processes.pop_head().unwrap());
102 }
103 None => {
104 self.processes.push_tail(self.processes.pop_head().unwrap());
105 }
106 }
107 }
108
109 let next = match next {
110 Some(p) => p,
111 None => {
112 return SchedulingDecision::TrySleep;
114 }
115 };
116
117 let timeslice = if self.last_rescheduled.get() {
118 self.time_remaining.get()
119 } else {
120 self.time_remaining.set(self.timeslice_length);
122 self.timeslice_length
123 };
124 let non_zero_timeslice = NonZeroU32::new(timeslice).unwrap();
126
127 SchedulingDecision::RunProcess((next, Some(non_zero_timeslice)))
128 }
129
130 fn result(&self, result: StoppedExecutingReason, execution_time_us: Option<u32>) {
131 let execution_time_us = execution_time_us.unwrap(); let reschedule = match result {
133 StoppedExecutingReason::KernelPreemption => {
134 if self.time_remaining.get() > execution_time_us {
135 self.time_remaining
136 .set(self.time_remaining.get() - execution_time_us);
137 true
138 } else {
139 false
140 }
141 }
142 _ => false,
143 };
144 self.last_rescheduled.set(reschedule);
145 if !reschedule {
146 self.processes.push_tail(self.processes.pop_head().unwrap());
147 }
148 }
149}