use types::{ bounded::{CapSlot, Priority}, id::{CoreId, ProcessId, ThreadId}, time::TimeSlice, }; use crate::error::KernelError; #[cfg(test)] #[path = "tests.rs"] mod tests; pub struct Thread { id: ThreadId, process: ProcessId, state: ThreadState, saved_context: SavedContext, priority: Priority, time_slice: TimeSlice, core_affinity: Option, } pub enum ThreadState { Ready, Running(CoreId), Blocked(BlockReason), Dead, } pub struct SavedContext { registers: [usize; 32], pc: usize, status: usize, } impl SavedContext { pub fn empty() -> Self { Self { registers: [0; 32], pc: 0, status: 0, } } pub fn with_entry_and_stack( entry: types::address::EntryPoint, stack: types::address::StackPointer, ) -> Self { let mut ctx = Self::empty(); ctx.pc = entry.raw(); ctx.registers[2] = stack.raw(); ctx } pub fn program_counter(&self) -> usize { self.pc } pub fn set_pc(&mut self, pc: usize) { self.pc = pc; } pub fn status(&self) -> usize { self.status } pub fn set_status(&mut self, status: usize) { self.status = status; } pub fn registers(&self) -> &[usize; 32] { &self.registers } pub fn registers_mut(&mut self) -> &mut [usize; 32] { &mut self.registers } } pub enum BlockReason { Sending(CapSlot), Receiving(CapSlot), WaitingForInterrupt(CapSlot), WaitingAny, } impl Thread { pub fn new( id: ThreadId, process: ProcessId, priority: Priority, time_slice: TimeSlice, core_affinity: Option, ) -> Self { Self { id, process, state: ThreadState::Ready, saved_context: SavedContext::empty(), priority, time_slice, core_affinity, } } pub fn with_context( id: ThreadId, process: ProcessId, priority: Priority, time_slice: TimeSlice, core_affinity: Option, context: SavedContext, ) -> Self { Self { id, process, state: ThreadState::Ready, saved_context: context, priority, time_slice, core_affinity, } } pub fn saved_context(&self) -> &SavedContext { &self.saved_context } pub fn saved_context_mut(&mut self) -> &mut SavedContext { &mut self.saved_context } pub fn id(&self) -> ThreadId { self.id } pub fn process(&self) -> ProcessId { self.process } pub fn state(&self) -> &ThreadState { &self.state } pub fn priority(&self) -> Priority { self.priority } pub fn time_slice(&self) -> &TimeSlice { &self.time_slice } pub fn time_slice_mut(&mut self) -> &mut TimeSlice { &mut self.time_slice } pub fn core_affinity(&self) -> Option { self.core_affinity } pub fn set_running(&mut self, core: CoreId) -> Result<(), KernelError> { match self.state { ThreadState::Ready => { self.state = ThreadState::Running(core); Ok(()) } _ => Err(KernelError::InvalidStateTransition(self.id)), } } pub fn block(&mut self, reason: BlockReason) -> Result<(), KernelError> { match self.state { ThreadState::Running(_) => { self.state = ThreadState::Blocked(reason); Ok(()) } _ => Err(KernelError::InvalidStateTransition(self.id)), } } pub fn unblock(&mut self) -> Result<(), KernelError> { match self.state { ThreadState::Blocked(_) => { self.state = ThreadState::Ready; Ok(()) } _ => Err(KernelError::InvalidStateTransition(self.id)), } } pub fn preempt(&mut self) -> Result<(), KernelError> { match self.state { ThreadState::Running(_) => { self.state = ThreadState::Ready; Ok(()) } _ => Err(KernelError::InvalidStateTransition(self.id)), } } pub fn exit(&mut self) -> Result<(), KernelError> { match self.state { ThreadState::Running(_) => { self.state = ThreadState::Dead; Ok(()) } _ => Err(KernelError::InvalidStateTransition(self.id)), } } } pub const MAX_THREADS: usize = 256; pub struct ThreadTable { threads: [Option; MAX_THREADS], } impl ThreadTable { pub fn new() -> Self { Self { threads: [const { None }; MAX_THREADS], } } pub fn spawn( &mut self, process: ProcessId, priority: Priority, time_slice: TimeSlice, core_affinity: Option, ) -> Result { let idx = self .threads .iter() .position(|s| s.is_none()) .ok_or(KernelError::TooManyThreads)?; let id = ThreadId::from_raw(idx as u16); self.threads[idx] = Some(Thread::new( id, process, priority, time_slice, core_affinity, )); Ok(id) } pub fn spawn_with_context( &mut self, process: ProcessId, priority: Priority, time_slice: TimeSlice, core_affinity: Option, context: SavedContext, ) -> Result { let idx = self .threads .iter() .position(|s| s.is_none()) .ok_or(KernelError::TooManyThreads)?; let id = ThreadId::from_raw(idx as u16); self.threads[idx] = Some(Thread::with_context( id, process, priority, time_slice, core_affinity, context, )); Ok(id) } pub fn get(&self, id: ThreadId) -> Result<&Thread, KernelError> { let idx = id.raw() as usize; self.threads .get(idx) .and_then(|s| s.as_ref()) .ok_or(KernelError::InvalidThread(id)) } pub fn get_mut(&mut self, id: ThreadId) -> Result<&mut Thread, KernelError> { let idx = id.raw() as usize; self.threads .get_mut(idx) .and_then(|s| s.as_mut()) .ok_or(KernelError::InvalidThread(id)) } pub fn destroy(&mut self, id: ThreadId) -> Result { let idx = id.raw() as usize; self.threads .get_mut(idx) .and_then(|s| s.take()) .ok_or(KernelError::InvalidThread(id)) } pub fn threads_for_process(&self, process: ProcessId) -> impl Iterator { self.threads .iter() .filter_map(|s| s.as_ref()) .filter(move |t| t.process() == process) } } impl Default for ThreadTable { fn default() -> Self { Self::new() } }