//! Fixed-capacity table of threads. use types::{ bounded::Priority, generation::Generation, id::{CoreId, ProcessId, ThreadId}, time::TimeSlice, }; use super::{SavedContext, Thread}; use crate::{error::KernelError, generations::SlotGenerations}; pub const MAX_THREADS: usize = if cfg!(kani) { 3 } else { 256 }; pub struct ThreadTable { threads: [Option; MAX_THREADS], generations: SlotGenerations, } impl ThreadTable { pub fn new() -> Self { Self { threads: [const { None }; MAX_THREADS], generations: SlotGenerations::new(), } } /// Current generation of a live thread, so references to it (reply /// slots) can detect that the id was reused. pub fn generation(&self, id: ThreadId) -> Option { self.get(id).ok()?; self.generations.current(id.raw() as usize) } fn free_slot(&self) -> Option { self.threads .iter() .enumerate() .position(|(i, t)| t.is_none() && self.generations.is_usable(i)) } pub fn spawn( &mut self, process: ProcessId, priority: Priority, time_slice: TimeSlice, core_affinity: Option, ) -> Result { let idx = self.free_slot().ok_or(KernelError::TooManyThreads)?; let id = ThreadId::from_raw(idx as u16); *self .threads .get_mut(idx) .ok_or(KernelError::TooManyThreads)? = 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.free_slot().ok_or(KernelError::TooManyThreads)?; let id = ThreadId::from_raw(idx as u16); *self .threads .get_mut(idx) .ok_or(KernelError::TooManyThreads)? = Some(Thread::with_context( id, process, priority, time_slice, core_affinity, context, )); Ok(id) } /// Every live thread with its id. pub fn iter(&self) -> impl Iterator { self.threads .iter() .enumerate() .filter_map(|(i, t)| Some((ThreadId::from_raw(i as u16), t.as_ref()?))) } 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; let thread = self .threads .get_mut(idx) .and_then(|s| s.take()) .ok_or(KernelError::InvalidThread(id))?; self.generations.advance(idx); Ok(thread) } 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() } }