kernel/sched/scheduler.rs
1//! Scheduler module
2//!
3//! The scheduler module is responsible for scheduling tasks on the CPU.
4//! Currently, the scheduler is a simple round-robin scheduler with separate
5//! queues for different task states to improve efficiency:
6//!
7//! - `ready_queue`: Tasks that are ready to run
8//! - `blocked_queue`: Tasks waiting for I/O or other events
9//! - `zombie_queue`: Finished tasks waiting to be cleaned up
10//!
11//! This separation avoids unnecessary iteration over blocked/zombie tasks
12//! during normal scheduling operations.
13
14extern crate alloc;
15
16use core::panic;
17
18use alloc::{collections::vec_deque::VecDeque, string::ToString};
19
20use crate::{arch::{enable_interrupt, get_cpu, get_user_trap_handler, instruction::idle, interrupt::enable_external_interrupts, set_trapframe, set_trapvector, trap::user::arch_switch_to_user_space, Arch}, environment::NUM_OF_CPUS, task::{new_kernel_task, wake_parent_waiters, wake_task_waiters, TaskState}, timer::get_kernel_timer, vm::{get_kernel_vm_manager, get_trampoline_trap_vector, get_trampoline_trapframe}};
21use crate::println;
22use crate::print;
23
24
25use super::dispatcher::Dispatcher;
26use crate::task::Task;
27
28static mut SCHEDULER: Option<Scheduler> = None;
29
30pub fn get_scheduler() -> &'static mut Scheduler {
31 unsafe {
32 match SCHEDULER {
33 Some(ref mut s) => s,
34 None => {
35 SCHEDULER = Some(Scheduler::new());
36 get_scheduler()
37 }
38 }
39 }
40}
41
42pub struct Scheduler {
43 /// Queue for ready-to-run tasks
44 ready_queue: [VecDeque<Task>; NUM_OF_CPUS],
45 /// Queue for blocked tasks (waiting for I/O, etc.)
46 blocked_queue: [VecDeque<Task>; NUM_OF_CPUS],
47 /// Queue for zombie tasks (finished but not yet cleaned up)
48 zombie_queue: [VecDeque<Task>; NUM_OF_CPUS],
49 dispatcher: [Dispatcher; NUM_OF_CPUS],
50 interval: u64, /* in microseconds */
51 current_task_id: [Option<usize>; NUM_OF_CPUS],
52}
53
54impl Scheduler {
55 pub const fn new() -> Self {
56 Scheduler {
57 ready_queue: [const { VecDeque::new() }; NUM_OF_CPUS],
58 blocked_queue: [const { VecDeque::new() }; NUM_OF_CPUS],
59 zombie_queue: [const { VecDeque::new() }; NUM_OF_CPUS],
60 dispatcher: [const { Dispatcher::new() }; NUM_OF_CPUS],
61 interval: 10000, /* 1ms */
62 current_task_id: [const { None }; NUM_OF_CPUS],
63 }
64 }
65
66 pub fn add_task(&mut self, task: Task, cpu_id: usize) {
67 // Add new tasks to the ready queue by default
68 self.ready_queue[cpu_id].push_back(task);
69 }
70
71 fn run(&mut self, cpu: &mut Arch) {
72 let cpu_id = cpu.get_cpuid();
73
74 // Continue trying to run tasks until we successfully dispatch one or run out of ready tasks
75 loop {
76 let task = self.ready_queue[cpu_id].pop_front();
77
78 /* If there are no subsequent tasks */
79 if self.ready_queue[cpu_id].is_empty() {
80 match task {
81 Some(mut t) => {
82 match t.state {
83 TaskState::Zombie => {
84 let task_id = t.get_id();
85 let parent_id = t.get_parent_id();
86 self.zombie_queue[cpu_id].push_back(t);
87 // crate::println!("Scheduler: Task {} is now a zombie", task_id);
88 self.current_task_id[cpu_id] = None;
89 // Wake up any processes waiting for this specific task
90 wake_task_waiters(task_id);
91 // Also wake up parent process for waitpid(-1)
92 if let Some(parent_id) = parent_id {
93 wake_parent_waiters(parent_id);
94 }
95 continue;
96 // panic!("At least one task must be scheduled");
97 },
98 TaskState::Terminated => {
99 panic!("At least one task must be scheduled");
100 },
101 TaskState::Blocked(_) => {
102 // Reset current_task_id since this task is no longer current
103 if self.current_task_id[cpu_id] == Some(t.get_id()) {
104 self.current_task_id[cpu_id] = None;
105 }
106 // Put blocked task to blocked queue without running it
107 self.blocked_queue[cpu_id].push_back(t);
108 continue;
109 },
110 _ => {
111 if self.current_task_id[cpu_id] != Some(t.get_id()) {
112 self.dispatcher[cpu_id].dispatch(cpu, &mut t);
113 }
114 self.current_task_id[cpu_id] = Some(t.get_id());
115 self.ready_queue[cpu_id].push_back(t);
116 break;
117 }
118 }
119 }
120 // If no tasks are ready, we can either go idle or wait for an interrupt
121 None => {
122 // panic!("MUST NOT reach here: No tasks ready to run");
123 // crate::println!("[Warning] Scheduler: No tasks ready, going idle");
124 // crate::println!("[Warning] This is wrong, there should always be at least one task (idle task) ready to run");
125 // crate::println!("[Warning] Creating idle task for CPU {}", cpu_id);
126 let mut kernel_task = new_kernel_task("idle".to_string(), 0, || {
127 // Idle loop
128 loop {
129 // Wait for an interrupt to wake up
130 enable_external_interrupts();
131 idle();
132 }
133 });
134 kernel_task.init();
135 // Add idle task to the ready queue
136 self.ready_queue[cpu_id].push_back(kernel_task);
137 }
138 }
139 } else {
140 match task {
141 Some(mut t) => {
142 match t.state {
143 TaskState::Zombie => {
144 let task_id = t.get_id();
145 let parent_id = t.get_parent_id();
146 self.zombie_queue[cpu_id].push_back(t);
147 // Wake up any processes waiting for this specific task
148 wake_task_waiters(task_id);
149 // Also wake up parent process for waitpid(-1)
150 if let Some(parent_id) = parent_id {
151 wake_parent_waiters(parent_id);
152 }
153 continue;
154 },
155 TaskState::Terminated => {
156 continue;
157 },
158 TaskState::Blocked(_) => {
159 // crate::println!("Scheduler: Task {} is blocked, moving to blocked queue", t.get_id());
160 // Reset current_task_id since this task is no longer current
161 if self.current_task_id[cpu_id] == Some(t.get_id()) {
162 self.current_task_id[cpu_id] = None;
163 }
164 // Put blocked task back to the end of queue without running it
165 self.blocked_queue[cpu_id].push_back(t);
166 continue;
167 },
168 _ => {
169 // Simply dispatch the task without prev_task logic
170 self.dispatcher[cpu_id].dispatch(cpu, &mut t);
171 self.current_task_id[cpu_id] = Some(t.get_id());
172 self.ready_queue[cpu_id].push_back(t);
173 break;
174 }
175 }
176 }
177 None => break,
178 }
179 }
180 }
181 }
182
183 /// Schedule tasks on the CPU, saving the currently running task's state
184 ///
185 /// This function is called by the timer interrupt handler. It saves the current
186 /// task's state and switches to the next task.
187 ///
188 /// # Arguments
189 /// * `cpu` - The CPU architecture state
190 pub fn schedule(&mut self, cpu: &mut Arch) -> ! {
191 let cpu_id = cpu.get_cpuid();
192
193 let timer = get_kernel_timer();
194 timer.stop(cpu_id);
195 timer.set_interval_us(cpu_id, self.interval);
196
197 // Save current task state if there is one
198 if let Some(current_task_id) = self.current_task_id[cpu_id] {
199 if let Some(current_task) = self.ready_queue[cpu_id].iter_mut().find(|t| t.get_id() == current_task_id) {
200 current_task.vcpu.store(cpu);
201 }
202 }
203
204 if !self.ready_queue[cpu_id].is_empty() {
205 self.run(cpu);
206 timer.start(cpu_id);
207 arch_switch_to_user_space(cpu);
208 }
209 // If the task queue is empty, go to idle
210 timer.start(cpu_id);
211 idle();
212 }
213
214 /// Schedule tasks on the CPU without current task (for initial startup)
215 ///
216 /// This function is called at initial startup when there is no current task
217 /// to save state for. It should only be used during system initialization.
218 ///
219 /// # Arguments
220 /// * `cpu` - The CPU architecture state
221 pub fn schedule_initial(&mut self, cpu: &mut Arch) -> ! {
222 let cpu_id = cpu.get_cpuid();
223
224 let timer = get_kernel_timer();
225 timer.stop(cpu_id);
226 timer.set_interval_us(cpu_id, self.interval);
227
228 // No current task state to save during initial startup
229
230 if !self.ready_queue[cpu_id].is_empty() {
231 self.run(cpu);
232 timer.start(cpu_id);
233 arch_switch_to_user_space(cpu);
234 }
235 // If the task queue is empty, go to idle
236 timer.start(cpu_id);
237 idle();
238 }
239
240 /* MUST NOT raise any exception in this function before the idle loop */
241 pub fn start_scheduler(&mut self) {
242 let cpu = get_cpu();
243 let cpu_id = cpu.get_cpuid();
244 let timer = get_kernel_timer();
245 timer.stop(cpu_id);
246
247 let trap_vector = get_trampoline_trap_vector();
248 let trapframe = get_trampoline_trapframe(cpu_id);
249 set_trapvector(trap_vector);
250 set_trapframe(trapframe);
251 // cpu.get_trapframe().set_trap_handler(get_user_trap_handler());
252 let trapframe = cpu.get_trapframe();
253 trapframe.set_trap_handler(get_user_trap_handler());
254 trapframe.set_next_address_space(get_kernel_vm_manager().get_asid());
255
256 /* Jump to trap handler immediately */
257 timer.set_interval_us(cpu_id, 0);
258 enable_interrupt();
259 timer.start(cpu_id);
260 idle();
261 }
262
263 pub fn get_current_task(&mut self, cpu_id: usize) -> Option<&mut Task> {
264 match self.current_task_id[cpu_id] {
265 Some(task_id) => self.ready_queue[cpu_id].iter_mut().find(|t| t.get_id() == task_id),
266 None => None
267 }
268 }
269
270 /// Returns a mutable reference to the task with the specified ID, if found.
271 ///
272 /// This method searches across all task queues (ready, blocked, zombie) to find
273 /// the task with the specified ID. This is needed for Waker integration.
274 ///
275 /// # Arguments
276 /// * `task_id` - The ID of the task to search for.
277 ///
278 /// # Returns
279 /// A mutable reference to the task if found, or None otherwise.
280 pub fn get_task_by_id(&mut self, task_id: usize) -> Option<&mut Task> {
281 // Search in ready queues
282 for ready_queue in self.ready_queue.iter_mut() {
283 if let Some(task) = ready_queue.iter_mut().find(|t| t.get_id() == task_id) {
284 return Some(task);
285 }
286 }
287
288 // Search in blocked queues
289 for blocked_queue in self.blocked_queue.iter_mut() {
290 if let Some(task) = blocked_queue.iter_mut().find(|t| t.get_id() == task_id) {
291 return Some(task);
292 }
293 }
294
295 // Search in zombie queues
296 for zombie_queue in self.zombie_queue.iter_mut() {
297 if let Some(task) = zombie_queue.iter_mut().find(|t| t.get_id() == task_id) {
298 return Some(task);
299 }
300 }
301
302 None
303 }
304
305 /// Move a task from blocked queue to ready queue when it's woken up
306 ///
307 /// This method is called by Waker when a blocked task needs to be woken up.
308 ///
309 /// # Arguments
310 /// * `task_id` - The ID of the task to move to ready queue
311 ///
312 /// # Returns
313 /// true if the task was found and moved, false otherwise
314 pub fn wake_task(&mut self, task_id: usize) -> bool {
315 // crate::println!("Scheduler: Waking up task {}", task_id);
316 // Search for the task in blocked queues
317 for cpu_id in 0..self.blocked_queue.len() {
318 if let Some(pos) = self.blocked_queue[cpu_id].iter().position(|t| t.get_id() == task_id) {
319 if let Some(mut task) = self.blocked_queue[cpu_id].remove(pos) {
320 // Set task state to Running
321 task.state = TaskState::Running;
322 // crate::println!("Scheduler: Task {} waking up with PC: 0x{:x}", task_id, task.vcpu.get_pc());
323 // Move to ready queue
324 self.ready_queue[cpu_id].push_back(task);
325 // crate::println!("Scheduler: Woke up task {} and moved it to ready queue", task_id);
326 return true;
327 }
328 }
329 }
330 false
331 }
332}
333
334pub fn make_test_tasks() {
335 println!("Making test tasks...");
336 let sched = get_scheduler();
337 let mut task0 = new_kernel_task("Task0".to_string(), 0, || {
338 println!("Task0");
339 let mut counter: usize = 0;
340 loop {
341 if counter % 500000 == 0 {
342 print!("\nTask0: ");
343 }
344 if counter % 10000 == 0 {
345 print!(".");
346 }
347 counter += 1;
348 if counter >= 100000000 {
349 break;
350 }
351 }
352 println!("");
353 println!("Task0: Done");
354 idle();
355 });
356 task0.init();
357 sched.add_task(task0, 0);
358
359 let mut task1 = new_kernel_task("Task1".to_string(), 0, || {
360 println!("Task1");
361 let mut counter: usize = 0;
362 loop {
363 if counter % 500000 == 0 {
364 print!("\nTask1: {} %", counter / 1000000);
365 }
366 counter += 1;
367 if counter >= 100000000 {
368 break;
369 }
370 }
371 println!("\nTask1: 100 %");
372 println!("Task1: Completed");
373 idle();
374 });
375 task1.init();
376 sched.add_task(task1, 0);
377
378 let mut task2 = new_kernel_task("Task2".to_string(), 0, || {
379 println!("Task2");
380 /* Fizz Buzz */
381 for i in 1..=1000000 {
382 if i % 1000 > 0 {
383 continue;
384 }
385 let c = i / 1000;
386 if c % 15 == 0 {
387 println!("FizzBuzz");
388 } else if c % 3 == 0 {
389 println!("Fizz");
390 } else if c % 5 == 0 {
391 println!("Buzz");
392 } else {
393 println!("{}", c);
394 }
395 }
396 println!("Task2: Done");
397 idle();
398 });
399 task2.init();
400 sched.add_task(task2, 0);
401}
402
403// late_initcall!(make_test_tasks);
404
405#[cfg(test)]
406mod tests {
407 use crate::task::TaskType;
408
409 use super::*;
410
411 #[test_case]
412 fn test_add_task() {
413 let mut scheduler = Scheduler::new();
414 let task = Task::new("TestTask".to_string(), 1, TaskType::Kernel);
415 scheduler.add_task(task, 0);
416 assert_eq!(scheduler.ready_queue[0].len(), 1);
417 }
418}