1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
//! Multicore Round-robin Scheduling.
//!
//! In this part, you will implement round robin scheduler.
//! Round robin is a scheduler that assigns equal amount of time slice into threads in circular order.
//! When time slice is expired, the scheduler kicks out the thread from the running state
//! and push back to the scheduling queue.
//!
//! ## Background
//! Thread is an abstraction of a cpu core.
//! Through the thread abstraction, the operating system can run multiple tasks
//! on a cpu core.
//!
//! KeOS already implemented some thread functionalities like thread creation,
//! thread switching.
//! You can create a new thread with [`ThreadBuilder`]. You provide a function to be run on
//! the thread as an argument to [`ThreadBuilder::spawn`].
//! When the first time the thread runs, the function executed.
//! When the function is terminated, the thread also be terminated.
//! Each thread, therefore, acts like a mini-program running inside the kernel.
//!
//! At any given time, exactly one thread runs and the rest, if any, become inactive.
//! The scheduler decides which thread to run next by calling [`Scheduler::next_to_run`].
//! If no thread is ready to run at any given time, then the special idle thread runs.
//!
//! The magics of a context switch lies on [`Thread::run`]. It saves the state of the
//! currently running thread and restores the state of the thread we're switching to.
//!
//! ## IMPORTANT NOTES
//! In KeOS, each thread is assigned a [`STACK_SIZE`]-size execution stack. The KeOS try to detect
//! the stack overflow, however it is not perfect. If the stack is overflowed, you can encounter a
//! mysterious kernel panics.
//! To prevent the such situation, DO NOT DECLARE large data structures (e.g. `let v: [u8; 0x200000];`)
//! and allocate them on heap through [`Box`].
//!
//! ## Implementation requirements
//! In this projects, you implements a round robin scheduler with 5ms time slices.
//! Note that [`Scheduler::timer_tick`] is called at every 1ms on each cpu.
//! When a new thread is reached, it must be pushed back to the current scheduling queue.
//! You are required to implement passive job stealing; when there is no task in the current runqueue, then steal from the other.
//!
//! ## Implementation Order
//! You will fill the [`Scheduler`] trait implementor for `RoundRobin` struct.
//! Fill the `todo!()`s of RoundRobin scheduler.
//! Read the documentation of [`Scheduler`] to understand what each member does.
//! The project is straightforward. You will meet several `todo!()`s during implementation,
//! and replace those `todo!()`s to your implementation.
//!
//!
//! [`RoundRobin`]: RoundRobin
//! [`Thread`]: keos::thread::Thread
//! [`Thread::run`]: keos::thread::Thread::run
//! [`Box`]: https://doc.rust-lang.org/alloc/boxed/struct.Box.html
//! [`STACK_SIZE`]: keos::thread::STACK_SIZE
//! [`ThreadBuilder`]: keos::thread::ThreadBuilder
//! [`ThreadBuilder::spawn`]: keos::thread::ThreadBuilder::spawn
//! [`Scheduler`]: keos::thread::scheduler::Scheduler
//! [`Scheduler::next_to_run`]: keos::thread::scheduler::Scheduler::next_to_run
use alloc::{boxed::Box, collections::VecDeque};
use core::sync::atomic::{AtomicIsize, Ordering};
use keos::{
intrinsics::cpuid,
sync::SpinLock,
thread::{scheduler::Scheduler, Thread},
MAX_CPU,
};
/// A round robin scheduler.
pub struct RoundRobin {
// Define any member you need.
}
unsafe impl Send for RoundRobin {}
unsafe impl Sync for RoundRobin {}
impl RoundRobin {
/// Create a new roundrobin scheduler.
pub fn new() -> Self {
todo!()
}
}
impl Scheduler for RoundRobin {
fn next_to_run(&self) -> Option<Box<Thread>> {
todo!()
}
fn push_to_queue(&self, thread: Box<Thread>) {
todo!()
}
fn timer_tick(&self) {
todo!()
}
}