You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							221 lines
						
					
					
						
							6.9 KiB
						
					
					
				
			
		
		
	
	
							221 lines
						
					
					
						
							6.9 KiB
						
					
					
				| 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<Tid>,
 | |
|     context: Option<Box<Context>>,
 | |
| }
 | |
| 
 | |
| 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<Mutex<Option<Thread>>>,
 | |
|     scheduler: Box<Scheduler>,
 | |
|     timer: Mutex<Timer<Event>>,
 | |
| }
 | |
| 
 | |
| 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<Option<Thread>>) {
 | |
|         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<Context>) -> 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<Tid>) -> 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<Context>)> {
 | |
|         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<Context>) {
 | |
|         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 process"),
 | |
|                 (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<Status> {
 | |
|         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<T: Default>(size: usize) -> Vec<T> {
 | |
|     let mut vec = Vec::new();
 | |
|     vec.resize_default(size);
 | |
|     vec
 | |
| }
 |