use alloc::boxed::Box; use alloc::sync::Arc; use log::*; use core::cell::UnsafeCell; use crate::thread_pool::*; use crate::interrupt; /// Thread executor /// /// Per-CPU struct. Defined at global. /// Only accessed by associated CPU with interrupt disabled. #[derive(Default)] pub struct Processor { inner: UnsafeCell>, } unsafe impl Sync for Processor {} struct ProcessorInner { id: usize, proc: Option<(Tid, Box)>, loop_context: Box, manager: Arc, } impl Processor { pub const fn new() -> Self { Processor { inner: UnsafeCell::new(None) } } pub unsafe fn init(&self, id: usize, context: Box, manager: Arc) { *self.inner.get() = Some(ProcessorInner { id, proc: None, loop_context: context, manager, }); } fn inner(&self) -> &mut ProcessorInner { unsafe { &mut *self.inner.get() }.as_mut() .expect("Processor is not initialized") } /// Begin running processes after CPU setup. /// /// This function never returns. It loops, doing: /// - choose a process to run /// - switch to start running that process /// - eventually that process transfers control /// via switch back to the scheduler. pub fn run(&self) -> ! { let inner = self.inner(); unsafe { interrupt::disable_and_store(); } loop { if let Some(proc) = inner.manager.run(inner.id) { trace!("CPU{} begin running thread {}", inner.id, proc.0); inner.proc = Some(proc); unsafe { inner.loop_context.switch_to(&mut *inner.proc.as_mut().unwrap().1); } let (pid, context) = inner.proc.take().unwrap(); trace!("CPU{} stop running thread {}", inner.id, pid); inner.manager.stop(pid, context); } else { trace!("CPU{} idle", inner.id); unsafe { interrupt::enable_and_wfi(); } // wait for a timer interrupt unsafe { interrupt::disable_and_store(); } } } } /// Called by process running on this Processor. /// Yield and reschedule. /// /// The interrupt may be enabled. pub fn yield_now(&self) { let inner = self.inner(); unsafe { let flags = interrupt::disable_and_store(); inner.proc.as_mut().unwrap().1.switch_to(&mut *inner.loop_context); interrupt::restore(flags); } } pub fn tid(&self) -> Tid { self.inner().proc.as_ref().unwrap().0 } pub fn context(&self) -> &Context { &*self.inner().proc.as_ref().unwrap().1 } pub fn manager(&self) -> &ThreadPool { &*self.inner().manager } /// Called by timer interrupt handler. /// /// The interrupt should be disabled in the handler. pub fn tick(&self) { // If I'm idle, tid == None, need_reschedule == false. // Will go back to `run()` after interrupt return. let tid = self.inner().proc.as_ref().map(|p| p.0); let need_reschedule = self.manager().tick(self.inner().id, tid); if need_reschedule { self.yield_now(); } } }