diff --git a/Cargo.toml b/Cargo.toml index 1f06456..e5d3a41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,4 @@ members = [ "crate/sync", "crate/thread", ] -exclude = ["kernel"] +exclude = ["kernel", "user/rust"] diff --git a/crate/thread/src/scheduler/mod.rs b/crate/thread/src/scheduler/mod.rs index 40ff440..bc42450 100644 --- a/crate/thread/src/scheduler/mod.rs +++ b/crate/thread/src/scheduler/mod.rs @@ -3,10 +3,12 @@ use alloc::{collections::BinaryHeap, vec::Vec}; use log::*; use spin::Mutex; +pub use self::o1::O1Scheduler; pub use self::rr::RRScheduler; pub use self::stride::StrideScheduler; pub use self::work_stealing::WorkStealingScheduler; +mod o1; mod rr; mod stride; mod work_stealing; diff --git a/crate/thread/src/scheduler/o1.rs b/crate/thread/src/scheduler/o1.rs new file mode 100644 index 0000000..f2e273c --- /dev/null +++ b/crate/thread/src/scheduler/o1.rs @@ -0,0 +1,65 @@ +//! O(1) scheduler introduced in Linux 2.6 +//! +//! Two queues are maintained, one is active, another is inactive. +//! Take the first task from the active queue to run. When it is empty, swap active and inactive queues. + +use super::*; + +pub struct O1Scheduler { + inner: Mutex, +} + +struct O1SchedulerInner { + active_queue: usize, + queues: [Vec; 2], +} + +impl Scheduler for O1Scheduler { + fn push(&self, tid: usize) { + self.inner.lock().push(tid); + } + fn pop(&self, _cpu_id: usize) -> Option { + self.inner.lock().pop() + } + fn tick(&self, current_tid: usize) -> bool { + self.inner.lock().tick(current_tid) + } + fn set_priority(&self, _tid: usize, _priority: u8) {} +} + +impl O1Scheduler { + pub fn new() -> Self { + let inner = O1SchedulerInner { + active_queue: 0, + queues: [Vec::new(), Vec::new()], + }; + O1Scheduler { + inner: Mutex::new(inner), + } + } +} + +impl O1SchedulerInner { + fn push(&mut self, tid: Tid) { + let inactive_queue = 1 - self.active_queue; + self.queues[inactive_queue].push(tid); + trace!("o1 push {}", tid - 1); + } + + fn pop(&mut self) -> Option { + let ret = match self.queues[self.active_queue].pop() { + Some(tid) => return Some(tid), + None => { + // active queue is empty, swap 'em + self.active_queue = 1 - self.active_queue; + self.queues[self.active_queue].pop() + } + }; + trace!("o1 pop {:?}", ret); + ret + } + + fn tick(&mut self, _current: Tid) -> bool { + true + } +} diff --git a/crate/thread/src/scheduler/stride.rs b/crate/thread/src/scheduler/stride.rs index e399c19..1d1ed71 100644 --- a/crate/thread/src/scheduler/stride.rs +++ b/crate/thread/src/scheduler/stride.rs @@ -1,3 +1,9 @@ +//! Stride scheduler +//! +//! Each task is assigned a priority. Each task has a running stride. +//! The task with least stride is selected to run. +//! When a task is rescheduled, its stride is added to proportional to 1 / priority. + use super::*; pub struct StrideScheduler { diff --git a/crate/thread/src/scheduler/work_stealing.rs b/crate/thread/src/scheduler/work_stealing.rs index e0a1757..b0cac58 100644 --- a/crate/thread/src/scheduler/work_stealing.rs +++ b/crate/thread/src/scheduler/work_stealing.rs @@ -1,5 +1,10 @@ +//! Work stealing scheduler +//! +//! Each CPU has its own queue, and each CPU takes new jobs from its own queue. +//! When its queue is empty, steal jobs from other CPU's queue. + use super::*; -use deque::{self, Stealer, Worker, Stolen}; +use deque::{self, Stealer, Stolen, Worker}; pub struct WorkStealingScheduler { /// The ready queue of each processors @@ -53,7 +58,12 @@ impl Scheduler for WorkStealingScheduler { Stolen::Abort => {} // retry Stolen::Empty => break, Stolen::Data(tid) => { - trace!("work-stealing: cpu{} steal thread {} from cpu{}", cpu_id, tid, other_id); + trace!( + "work-stealing: cpu{} steal thread {} from cpu{}", + cpu_id, + tid, + other_id + ); return Some(tid); } }