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}