diff --git a/crates/domain/src/error.rs b/crates/domain/src/error.rs index ed97982..6272965 100644 --- a/crates/domain/src/error.rs +++ b/crates/domain/src/error.rs @@ -8,4 +8,5 @@ pub enum KernelError { NoFreeSlot, InvalidProcess(ProcessId), WouldBlock, + TooManyThreadsInProcess, } diff --git a/crates/domain/src/process/mod.rs b/crates/domain/src/process/mod.rs new file mode 100644 index 0000000..cf9c0f2 --- /dev/null +++ b/crates/domain/src/process/mod.rs @@ -0,0 +1,73 @@ +use types::{ + bounded::CapSlot, + id::{ProcessId, ThreadId}, +}; + +use crate::capability::CapabilityTable; + +#[cfg(test)] +#[path = "tests.rs"] +mod tests; + +pub const MAX_THREADS_PER_PROCESS: usize = 4; + +pub struct Process { + id: ProcessId, + capabilities: CapabilityTable, + threads: [Option; MAX_THREADS_PER_PROCESS], + fault_handler: Option, +} + +impl Process { + pub fn new( + id: ProcessId, + capabilities: CapabilityTable, + fault_handler: Option, + ) -> Self { + Self { + id, + capabilities, + threads: [None; MAX_THREADS_PER_PROCESS], + fault_handler, + } + } + + pub fn id(&self) -> ProcessId { + self.id + } + + pub fn capabilities(&self) -> &CapabilityTable { + &self.capabilities + } + + pub fn capabilities_mut(&mut self) -> &mut CapabilityTable { + &mut self.capabilities + } + + pub fn fault_handler(&self) -> Option { + self.fault_handler + } + + pub fn add_thread(&mut self, thread: ThreadId) -> Result<(), crate::error::KernelError> { + let slot = self + .threads + .iter_mut() + .find(|s| s.is_none()) + .ok_or(crate::error::KernelError::TooManyThreadsInProcess)?; + *slot = Some(thread); + Ok(()) + } + + pub fn remove_thread(&mut self, thread: ThreadId) { + for slot in &mut self.threads { + if *slot == Some(thread) { + *slot = None; + return; + } + } + } + + pub fn thread_ids(&self) -> impl Iterator + '_ { + self.threads.iter().filter_map(|s| *s) + } +} diff --git a/crates/domain/src/process/tests.rs b/crates/domain/src/process/tests.rs new file mode 100644 index 0000000..3071dcc --- /dev/null +++ b/crates/domain/src/process/tests.rs @@ -0,0 +1,78 @@ +use super::*; +use crate::capability::CapabilityTable; +use types::bounded::CapSlot; +use types::id::{ProcessId, ThreadId}; + +fn thread(id: u16) -> ThreadId { + ThreadId::from_raw(id) +} + +fn test_process(id: u16) -> Process { + Process::new(ProcessId::from_raw(id), CapabilityTable::new(), None) +} + +#[test] +fn new_process_has_no_threads() { + let p = test_process(0); + assert_eq!(p.thread_ids().count(), 0); +} + +#[test] +fn id_matches_construction() { + let p = test_process(42); + assert_eq!(p.id(), ProcessId::from_raw(42)); +} + +#[test] +fn fault_handler_none_by_default() { + let p = test_process(0); + assert!(p.fault_handler().is_none()); +} + +#[test] +fn fault_handler_preserved() { + let slot = CapSlot::new(5).unwrap(); + let p = Process::new(ProcessId::from_raw(0), CapabilityTable::new(), Some(slot)); + assert_eq!(p.fault_handler(), Some(slot)); +} + +#[test] +fn add_and_iterate_threads() { + let mut p = test_process(0); + p.add_thread(thread(0)).unwrap(); + p.add_thread(thread(1)).unwrap(); + assert_eq!(p.thread_ids().count(), 2); + assert!(p.thread_ids().any(|t| t == thread(0))); + assert!(p.thread_ids().any(|t| t == thread(1))); +} + +#[test] +fn add_thread_beyond_max_fails() { + let mut p = test_process(0); + for i in 0..MAX_THREADS_PER_PROCESS { + p.add_thread(thread(i as u16)).unwrap(); + } + assert!(p.add_thread(thread(99)).is_err()); +} + +#[test] +fn remove_thread_frees_slot() { + let mut p = test_process(0); + p.add_thread(thread(0)).unwrap(); + p.remove_thread(thread(0)); + assert_eq!(p.thread_ids().count(), 0); + p.add_thread(thread(1)).unwrap(); + assert_eq!(p.thread_ids().count(), 1); +} + +#[test] +fn capabilities_accessible() { + let p = test_process(0); + let _caps = p.capabilities(); +} + +#[test] +fn capabilities_mut_accessible() { + let mut p = test_process(0); + let _caps = p.capabilities_mut(); +}