use alloc::boxed::Box; use alloc::vec::Vec; use spin::{Mutex, MutexGuard}; use log::*; use crate::scheduler::Scheduler; use crate::timer::Timer; struct Thread { status: Status, status_after_stop: Status, waiter: Option, context: Option>, } pub type Tid = usize; type ExitCode = usize; #[derive(Debug, Clone, Eq, PartialEq)] pub enum Status { Ready, Running(usize), Sleeping, /// aka ZOMBIE. Its context was dropped. Exited(ExitCode), } #[derive(Eq, PartialEq)] enum Event { Wakeup(Tid), } pub trait Context { /// Switch to target context unsafe fn switch_to(&mut self, target: &mut Context); /// A tid is allocated for this context fn set_tid(&mut self, tid: Tid); } pub struct ThreadPool { threads: Vec>>, scheduler: Box, timer: Mutex>, } impl ThreadPool { pub fn new(scheduler: impl Scheduler, max_proc_num: usize) -> Self { ThreadPool { threads: new_vec_default(max_proc_num), scheduler: Box::new(scheduler), timer: Mutex::new(Timer::new()), } } fn alloc_tid(&self) -> (Tid, MutexGuard>) { for (i, proc) in self.threads.iter().enumerate() { let thread = proc.lock(); if thread.is_none() { return (i, thread); } } panic!("Process number exceeded"); } /// Add a new thread /// Calls action with tid and thread context pub fn add(&self, mut context: Box) -> Tid { let (tid, mut thread) = self.alloc_tid(); context.set_tid(tid); *thread = Some(Thread { status: Status::Ready, status_after_stop: Status::Ready, waiter: None, context: Some(context), }); self.scheduler.push(tid); tid } /// Make process `tid` time slice -= 1. /// Return true if time slice == 0. /// Called by timer interrupt handler. pub(crate) fn tick(&self, cpu_id: usize, tid: Option) -> bool { if cpu_id == 0 { let mut timer = self.timer.lock(); timer.tick(); while let Some(event) = timer.pop() { match event { Event::Wakeup(tid) => self.set_status(tid, Status::Ready), } } } match 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.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)> { self.scheduler.pop(cpu_id) .map(|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); (tid, proc.context.take().expect("context not exist")) }) } /// Called by Processor to finish running a process /// and give its context back. pub(crate) fn stop(&self, tid: Tid, context: Box) { let mut proc_lock = self.threads[tid].lock(); let mut proc = proc_lock.as_mut().expect("process not exist"); proc.status = proc.status_after_stop.clone(); proc.status_after_stop = Status::Ready; proc.context = Some(context); match proc.status { Status::Ready => self.scheduler.push(tid), Status::Exited(_) => self.exit_handler(tid, proc), _ => {} } } /// Called by `JoinHandle` to let thread `tid` wait for `target`. /// The `tid` is going to sleep, and will be woke up when `target` exit. /// (see `exit_handler()`) pub(crate) fn wait(&self, tid: Tid, target: Tid) { self.set_status(tid, Status::Sleeping); let mut target_lock = self.threads[target].lock(); let target = target_lock.as_mut().expect("process not exist"); target.waiter = Some(tid); } /// Switch the status of a process. /// Insert/Remove it to/from scheduler if necessary. fn set_status(&self, tid: Tid, status: Status) { let mut proc_lock = self.threads[tid].lock(); if let Some(mut proc) = proc_lock.as_mut() { trace!("process {} {:?} -> {:?}", tid, proc.status, status); match (&proc.status, &status) { (Status::Ready, Status::Ready) => return, (Status::Ready, _) => panic!("can not remove a process from ready queue"), (Status::Exited(_), _) => panic!("can not set status for a exited thread"), (Status::Sleeping, Status::Exited(_)) => self.timer.lock().stop(Event::Wakeup(tid)), (Status::Running(_), Status::Ready) => {} // process will be added to scheduler in stop() (_, Status::Ready) => self.scheduler.push(tid), _ => {} } match proc.status { Status::Running(_) => proc.status_after_stop = status, _ => proc.status = status, } match proc.status { Status::Exited(_) => self.exit_handler(tid, proc), _ => {} } } } pub fn get_status(&self, tid: Tid) -> Option { if tid < self.threads.len() { self.threads[tid].lock().as_ref().map(|p| p.status.clone()) } else { None } } /// Remove an exited proc `tid`. pub fn remove(&self, tid: Tid) { let mut proc_lock = self.threads[tid].lock(); let proc = proc_lock.as_ref().expect("process not exist"); match proc.status { Status::Exited(_) => {} _ => panic!("can not remove non-exited process"), } // release the tid *proc_lock = None; } /// Sleep `tid` for `time` ticks. /// `time` == 0 means sleep forever pub fn sleep(&self, tid: Tid, time: usize) { self.set_status(tid, Status::Sleeping); if time != 0 { self.timer.lock().start(time, Event::Wakeup(tid)); } } pub fn wakeup(&self, tid: Tid) { self.set_status(tid, Status::Ready); } pub fn exit(&self, tid: Tid, code: ExitCode) { // NOTE: if `tid` is running, status change will be deferred. self.set_status(tid, Status::Exited(code)); } /// Called when a thread exit fn exit_handler(&self, _tid: Tid, proc: &mut Thread) { // wake up waiter if let Some(waiter) = proc.waiter { self.wakeup(waiter); } // drop its context proc.context = None; } } fn new_vec_default(size: usize) -> Vec { let mut vec = Vec::new(); vec.resize_default(size); vec }