diff --git a/crate/thread/src/lib.rs b/crate/thread/src/lib.rs index af97063..242c984 100644 --- a/crate/thread/src/lib.rs +++ b/crate/thread/src/lib.rs @@ -2,7 +2,6 @@ #![feature(alloc)] #![feature(const_fn)] #![feature(linkage)] -#![feature(nll)] #![feature(vec_resize_default)] #![feature(asm)] #![feature(exact_size_is_empty)] diff --git a/crate/thread/src/scheduler.rs b/crate/thread/src/scheduler.rs deleted file mode 100644 index a73960d..0000000 --- a/crate/thread/src/scheduler.rs +++ /dev/null @@ -1,242 +0,0 @@ -use alloc::{collections::BinaryHeap, vec::Vec}; -use log::*; - -type Pid = usize; - -/// -pub trait Scheduler { - fn insert(&mut self, pid: Pid); - fn remove(&mut self, pid: Pid); - fn select(&mut self) -> Option; - fn tick(&mut self, current: Pid) -> bool; // need reschedule? - fn set_priority(&mut self, pid: Pid, priority: u8); - fn move_to_head(&mut self, pid: Pid); -} - -pub use self::rr::RRScheduler; -pub use self::stride::StrideScheduler; - -mod rr { - use super::*; - - pub struct RRScheduler { - max_time_slice: usize, - infos: Vec, - } - - #[derive(Debug, Default, Copy, Clone)] - struct RRProcInfo { - present: bool, - rest_slice: usize, - prev: Pid, - next: Pid, - } - - impl Scheduler for RRScheduler { - fn insert(&mut self, pid: Pid) { - let pid = pid + 1; - expand(&mut self.infos, pid); - { - let info = &mut self.infos[pid]; - assert!(!info.present); - info.present = true; - if info.rest_slice == 0 { - info.rest_slice = self.max_time_slice; - } - } - self._list_add_before(pid, 0); - trace!("rr insert {}", pid - 1); - } - - fn remove(&mut self, pid: Pid) { - let pid = pid + 1; - assert!(self.infos[pid].present); - self.infos[pid].present = false; - self._list_remove(pid); - trace!("rr remove {}", pid - 1); - } - - fn select(&mut self) -> Option { - let ret = match self.infos[0].next { - 0 => None, - i => Some(i - 1), - }; - trace!("rr select {:?}", ret); - ret - } - - fn tick(&mut self, current: Pid) -> bool { - let current = current + 1; - expand(&mut self.infos, current); - assert!(!self.infos[current].present); - - let rest = &mut self.infos[current].rest_slice; - if *rest > 0 { - *rest -= 1; - } else { - warn!("current process rest_slice = 0, need reschedule") - } - *rest == 0 - } - - fn set_priority(&mut self, _pid: usize, _priority: u8) { - } - - fn move_to_head(&mut self, pid: usize) { - let pid = pid + 1; - assert!(self.infos[pid].present); - self._list_remove(pid); - self._list_add_after(pid, 0); - trace!("rr move_to_head {}", pid - 1); - } - } - - impl RRScheduler { - pub fn new(max_time_slice: usize) -> Self { - RRScheduler { - max_time_slice, - infos: Vec::default(), - } - } - fn _list_add_before(&mut self, i: Pid, at: Pid) { - let prev = self.infos[at].prev; - self.infos[i].next = at; - self.infos[i].prev = prev; - self.infos[prev].next = i; - self.infos[at].prev = i; - } - fn _list_add_after(&mut self, i: Pid, at: Pid) { - let next = self.infos[at].next; - self._list_add_before(i, next); - } - fn _list_remove(&mut self, i: Pid) { - let next = self.infos[i].next; - let prev = self.infos[i].prev; - self.infos[next].prev = prev; - self.infos[prev].next = next; - self.infos[i].next = 0; - self.infos[i].prev = 0; - } - } -} - -mod stride { - use super::*; - - pub struct StrideScheduler { - max_time_slice: usize, - infos: Vec, - queue: BinaryHeap<(Stride, Pid)>, // It's max heap, so pass < 0 - } - - #[derive(Debug, Default, Copy, Clone)] - struct StrideProcInfo { - present: bool, - rest_slice: usize, - stride: Stride, - priority: u8, - } - - impl StrideProcInfo { - fn pass(&mut self) { - const BIG_STRIDE: Stride = 1 << 20; - let pass = if self.priority == 0 { - BIG_STRIDE - } else { - BIG_STRIDE / self.priority as Stride - }; - // FIXME: overflowing_add is not working ??? - // self.stride.overflowing_add(pass); - self.stride += pass; - } - } - - type Stride = i32; - - impl Scheduler for StrideScheduler { - fn insert(&mut self, pid: Pid) { - expand(&mut self.infos, pid); - let info = &mut self.infos[pid]; - assert!(!info.present); - info.present = true; - if info.rest_slice == 0 { - info.rest_slice = self.max_time_slice; - } - self.queue.push((-info.stride, pid)); - trace!("stride insert {}", pid); - } - - fn remove(&mut self, pid: Pid) { - let info = &mut self.infos[pid]; - assert!(info.present); - info.present = false; - if self.queue.peek().is_some() && self.queue.peek().unwrap().1 == pid { - self.queue.pop(); - } else { - // BinaryHeap only support pop the top. - // So in order to remove an arbitrary element, - // we have to take all elements into a Vec, - // then push the rest back. - let rest: Vec<_> = self.queue.drain().filter(|&p| p.1 != pid).collect(); - use core::iter::FromIterator; - self.queue = BinaryHeap::from_iter(rest.into_iter()); - } - trace!("stride remove {}", pid); - } - - fn select(&mut self) -> Option { - let ret = self.queue.peek().map(|&(_, pid)| pid); - if let Some(pid) = ret { - let old_stride = self.infos[pid].stride; - self.infos[pid].pass(); - let stride = self.infos[pid].stride; - trace!("stride {} {:#x} -> {:#x}", pid, old_stride, stride); - } - trace!("stride select {:?}", ret); - ret - } - - fn tick(&mut self, current: Pid) -> bool { - expand(&mut self.infos, current); - assert!(!self.infos[current].present); - - let rest = &mut self.infos[current].rest_slice; - if *rest > 0 { - *rest -= 1; - } else { - warn!("current process rest_slice = 0, need reschedule") - } - *rest == 0 - } - - fn set_priority(&mut self, pid: Pid, priority: u8) { - self.infos[pid].priority = priority; - trace!("stride {} priority = {}", pid, priority); - } - - fn move_to_head(&mut self, pid: Pid) { - if self.queue.peek().is_some() { - let stride = -self.queue.peek().unwrap().0; - self.remove(pid); - self.infos[pid].stride = stride; - self.insert(pid); - } - } - } - - impl StrideScheduler { - pub fn new(max_time_slice: usize) -> Self { - StrideScheduler { - max_time_slice, - infos: Vec::default(), - queue: BinaryHeap::default(), - } - } - } -} - -fn expand(vec: &mut Vec, id: usize) { - let len = vec.len(); - vec.resize(len.max(id + 1), T::default()); -} - diff --git a/crate/thread/src/scheduler/mod.rs b/crate/thread/src/scheduler/mod.rs new file mode 100644 index 0000000..04e56fd --- /dev/null +++ b/crate/thread/src/scheduler/mod.rs @@ -0,0 +1,32 @@ +use alloc::{collections::BinaryHeap, vec::Vec}; + +use log::*; +use spin::Mutex; + +pub use self::rr::RRScheduler; +pub use self::stride::StrideScheduler; + +mod rr; +mod stride; + +type Pid = usize; + +/// The scheduler for a ThreadPool +pub trait Scheduler: Sync + 'static { + /// Push a thread to the back of ready queue. + fn push(&self, pid: Pid); + /// Remove a thread from the ready queue. + fn remove(&self, pid: Pid); + /// Select a thread to run, pop it from the queue. + fn pop(&self) -> Option; + /// Got a tick from CPU. + /// Return true if need reschedule. + fn tick(&self, current_pid: Pid) -> bool; + /// Set priority of a thread. + fn set_priority(&self, pid: Pid, priority: u8); +} + +fn expand(vec: &mut Vec, id: usize) { + let len = vec.len(); + vec.resize(len.max(id + 1), T::default()); +} diff --git a/crate/thread/src/scheduler/rr.rs b/crate/thread/src/scheduler/rr.rs new file mode 100644 index 0000000..eb79b76 --- /dev/null +++ b/crate/thread/src/scheduler/rr.rs @@ -0,0 +1,118 @@ +use super::*; + +pub struct RRScheduler { + inner: Mutex, +} + +struct RRSchedulerInner { + max_time_slice: usize, + infos: Vec, +} + +#[derive(Debug, Default, Copy, Clone)] +struct RRProcInfo { + present: bool, + rest_slice: usize, + prev: Pid, + next: Pid, +} + +impl Scheduler for RRScheduler { + fn push(&self, pid: usize) { + self.inner.lock().push(pid); + } + fn remove(&self, pid: usize) { + self.inner.lock().remove(pid); + } + fn pop(&self) -> Option { + self.inner.lock().pop() + } + fn tick(&self, current_pid: usize) -> bool { + self.inner.lock().tick(current_pid) + } + fn set_priority(&self, _pid: usize, _priority: u8) {} +} + +impl RRScheduler { + pub fn new(max_time_slice: usize) -> Self { + let inner = RRSchedulerInner { + max_time_slice, + infos: Vec::default(), + }; + RRScheduler { inner: Mutex::new(inner) } + } +} + +impl RRSchedulerInner { + fn push(&mut self, pid: Pid) { + let pid = pid + 1; + expand(&mut self.infos, pid); + { + let info = &mut self.infos[pid]; + assert!(!info.present); + info.present = true; + if info.rest_slice == 0 { + info.rest_slice = self.max_time_slice; + } + } + self._list_add_before(pid, 0); + trace!("rr push {}", pid - 1); + } + + fn remove(&mut self, pid: Pid) { + let pid = pid + 1; + assert!(self.infos[pid].present); + self.infos[pid].present = false; + self._list_remove(pid); + trace!("rr remove {}", pid - 1); + } + + fn pop(&mut self) -> Option { + let ret = match self.infos[0].next { + 0 => None, + pid => { + self.infos[pid].present = false; + self._list_remove(pid); + Some(pid - 1) + }, + }; + trace!("rr pop {:?}", ret); + ret + } + + fn tick(&mut self, current: Pid) -> bool { + let current = current + 1; + expand(&mut self.infos, current); + assert!(!self.infos[current].present); + + let rest = &mut self.infos[current].rest_slice; + if *rest > 0 { + *rest -= 1; + } else { + warn!("current process rest_slice = 0, need reschedule") + } + *rest == 0 + } +} + +impl RRSchedulerInner { + fn _list_add_before(&mut self, i: Pid, at: Pid) { + let prev = self.infos[at].prev; + self.infos[i].next = at; + self.infos[i].prev = prev; + self.infos[prev].next = i; + self.infos[at].prev = i; + } + fn _list_add_after(&mut self, i: Pid, at: Pid) { + let next = self.infos[at].next; + self._list_add_before(i, next); + } + fn _list_remove(&mut self, i: Pid) { + let next = self.infos[i].next; + let prev = self.infos[i].prev; + self.infos[next].prev = prev; + self.infos[prev].next = next; + self.infos[i].next = 0; + self.infos[i].prev = 0; + } +} diff --git a/crate/thread/src/scheduler/stride.rs b/crate/thread/src/scheduler/stride.rs new file mode 100644 index 0000000..12292a7 --- /dev/null +++ b/crate/thread/src/scheduler/stride.rs @@ -0,0 +1,126 @@ +use super::*; + +pub struct StrideScheduler { + inner: Mutex, +} + +pub struct StrideSchedulerInner { + max_time_slice: usize, + infos: Vec, + queue: BinaryHeap<(Stride, Pid)>, // It's max heap, so pass < 0 +} + +#[derive(Debug, Default, Copy, Clone)] +struct StrideProcInfo { + present: bool, + rest_slice: usize, + stride: Stride, + priority: u8, +} + +impl StrideProcInfo { + fn pass(&mut self) { + const BIG_STRIDE: Stride = 1 << 20; + let pass = if self.priority == 0 { + BIG_STRIDE + } else { + BIG_STRIDE / self.priority as Stride + }; + // FIXME: overflowing_add is not working ??? + // self.stride.overflowing_add(pass); + self.stride += pass; + } +} + +type Stride = i32; + +impl Scheduler for StrideScheduler { + fn push(&self, pid: usize) { + self.inner.lock().push(pid); + } + fn remove(&self, pid: usize) { + self.inner.lock().remove(pid); + } + fn pop(&self) -> Option { + self.inner.lock().pop() + } + fn tick(&self, current_pid: usize) -> bool { + self.inner.lock().tick(current_pid) + } + fn set_priority(&self, pid: usize, priority: u8) { + self.inner.lock().set_priority(pid, priority); + } +} + +impl StrideScheduler { + pub fn new(max_time_slice: usize) -> Self { + let inner = StrideSchedulerInner { + max_time_slice, + infos: Vec::default(), + queue: BinaryHeap::default(), + }; + StrideScheduler { inner: Mutex::new(inner) } + } +} + +impl StrideSchedulerInner { + fn push(&mut self, pid: Pid) { + expand(&mut self.infos, pid); + let info = &mut self.infos[pid]; + assert!(!info.present); + info.present = true; + if info.rest_slice == 0 { + info.rest_slice = self.max_time_slice; + } + self.queue.push((-info.stride, pid)); + trace!("stride push {}", pid); + } + + fn remove(&mut self, pid: Pid) { + let info = &mut self.infos[pid]; + assert!(info.present); + info.present = false; + if self.queue.peek().is_some() && self.queue.peek().unwrap().1 == pid { + self.queue.pop(); + } else { + // BinaryHeap only support pop the top. + // So in order to remove an arbitrary element, + // we have to take all elements into a Vec, + // then push the rest back. + let rest: Vec<_> = self.queue.drain().filter(|&p| p.1 != pid).collect(); + use core::iter::FromIterator; + self.queue = BinaryHeap::from_iter(rest.into_iter()); + } + trace!("stride remove {}", pid); + } + + fn pop(&mut self) -> Option { + let ret = self.queue.pop().map(|(_, pid)| pid); + if let Some(pid) = ret { + let old_stride = self.infos[pid].stride; + self.infos[pid].pass(); + let stride = self.infos[pid].stride; + trace!("stride {} {:#x} -> {:#x}", pid, old_stride, stride); + } + trace!("stride pop {:?}", ret); + ret + } + + fn tick(&mut self, current: Pid) -> bool { + expand(&mut self.infos, current); + assert!(!self.infos[current].present); + + let rest = &mut self.infos[current].rest_slice; + if *rest > 0 { + *rest -= 1; + } else { + warn!("current process rest_slice = 0, need reschedule") + } + *rest == 0 + } + + fn set_priority(&mut self, pid: Pid, priority: u8) { + self.infos[pid].priority = priority; + trace!("stride {} priority = {}", pid, priority); + } +} diff --git a/crate/thread/src/thread_pool.rs b/crate/thread/src/thread_pool.rs index 93d159f..487dc77 100644 --- a/crate/thread/src/thread_pool.rs +++ b/crate/thread/src/thread_pool.rs @@ -37,15 +37,15 @@ pub trait Context { pub struct ThreadPool { threads: Vec>>, - scheduler: Mutex>, + scheduler: Box, timer: Mutex>, } impl ThreadPool { - pub fn new(scheduler: Box, max_proc_num: usize) -> Self { + pub fn new(scheduler: impl Scheduler, max_proc_num: usize) -> Self { ThreadPool { threads: new_vec_default(max_proc_num), - scheduler: Mutex::new(scheduler), + scheduler: Box::new(scheduler), timer: Mutex::new(Timer::new()), } } @@ -69,7 +69,7 @@ impl ThreadPool { parent, children: Vec::new(), }); - self.scheduler.lock().insert(tid); + self.scheduler.push(tid); self.threads[parent].lock().as_mut().expect("invalid parent proc") .children.push(tid); tid @@ -89,24 +89,22 @@ impl ThreadPool { } } match tid { - Some(tid) => self.scheduler.lock().tick(tid), + Some(tid) => self.scheduler.tick(tid), None => false, } } /// Set the priority of process `tid` pub fn set_priority(&self, tid: Tid, priority: u8) { - self.scheduler.lock().set_priority(tid, priority); + self.scheduler.set_priority(tid, priority); } /// Called by Processor to get a process to run. /// The manager first mark it `Running`, /// then take out and return its Context. pub(crate) fn run(&self, cpu_id: usize) -> Option<(Tid, Box)> { - let mut scheduler = self.scheduler.lock(); - scheduler.select() + self.scheduler.pop() .map(|tid| { - scheduler.remove(tid); let mut proc_lock = self.threads[tid].lock(); let mut proc = proc_lock.as_mut().expect("process not exist"); proc.status = Status::Running(cpu_id); @@ -123,7 +121,7 @@ impl ThreadPool { proc.status_after_stop = Status::Ready; proc.context = Some(context); match proc.status { - Status::Ready => self.scheduler.lock().insert(tid), + Status::Ready => self.scheduler.push(tid), Status::Exited(_) => self.exit_handler(tid, proc), _ => {} } @@ -137,10 +135,10 @@ impl ThreadPool { trace!("process {} {:?} -> {:?}", tid, proc.status, status); match (&proc.status, &status) { (Status::Ready, Status::Ready) => return, - (Status::Ready, _) => self.scheduler.lock().remove(tid), + (Status::Ready, _) => self.scheduler.remove(tid), (Status::Exited(_), _) => panic!("can not set status for a exited process"), (Status::Sleeping, Status::Exited(_)) => self.timer.lock().stop(Event::Wakeup(tid)), - (_, Status::Ready) => self.scheduler.lock().insert(tid), + (_, Status::Ready) => self.scheduler.push(tid), _ => {} } match proc.status { diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 7626841..72e94fa 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -11,7 +11,7 @@ mod abi; pub fn init() { // NOTE: max_time_slice <= 5 to ensure 'priority' test pass - let scheduler = Box::new(scheduler::RRScheduler::new(5)); + let scheduler = scheduler::RRScheduler::new(5); let manager = Arc::new(ThreadPool::new(scheduler, MAX_PROCESS_NUM)); unsafe {