feat(domain): add Thread constructor, accessors, and state machine transitions

This commit is contained in:
2026-10-17 23:28:56 +02:00
parent 42ed5ade29
commit 6444de69b8
3 changed files with 307 additions and 1 deletions

View File

@@ -1,4 +1,8 @@
use types::{bounded::CapSlot, id::ProcessId, permission::Permission};
use types::{
bounded::CapSlot,
id::{ProcessId, ThreadId},
permission::Permission,
};
#[derive(Debug)]
pub enum KernelError {
@@ -10,4 +14,5 @@ pub enum KernelError {
WouldBlock,
TooManyThreadsInProcess,
TooManyProcesses,
InvalidStateTransition(ThreadId),
}

View File

@@ -0,0 +1,143 @@
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<CoreId>,
}
pub enum ThreadState {
Ready,
Running(CoreId),
Blocked(BlockReason),
Dead,
}
pub struct SavedContext {
registers: [usize; 32],
}
impl SavedContext {
pub fn empty() -> Self {
Self { registers: [0; 32] }
}
}
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<CoreId>,
) -> Self {
Self {
id,
process,
state: ThreadState::Ready,
saved_context: SavedContext::empty(),
priority,
time_slice,
core_affinity,
}
}
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<CoreId> {
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)),
}
}
}

View File

@@ -0,0 +1,158 @@
use super::*;
use types::bounded::Priority;
use types::id::{CoreId, ProcessId, ThreadId};
use types::time::TimeSlice;
fn thread(id: u16) -> ThreadId {
ThreadId::from_raw(id)
}
fn process(id: u16) -> ProcessId {
ProcessId::from_raw(id)
}
fn core(id: u8) -> CoreId {
CoreId::from_raw(id)
}
fn prio(value: u8) -> Priority {
Priority::new(value).unwrap()
}
fn time_slice() -> TimeSlice {
TimeSlice::new(10)
}
fn test_thread(id: u16, proc_id: u16) -> Thread {
Thread::new(thread(id), process(proc_id), prio(10), time_slice(), None)
}
// --- SavedContext tests ---
#[test]
fn saved_context_empty_creates_zeroed() {
let ctx = SavedContext::empty();
let _ = ctx;
}
// --- Thread construction tests ---
#[test]
fn new_thread_is_ready() {
let t = test_thread(0, 0);
assert!(matches!(t.state(), ThreadState::Ready));
}
#[test]
fn accessors_return_construction_values() {
let t = Thread::new(thread(5), process(3), prio(20), time_slice(), Some(core(1)));
assert_eq!(t.id(), thread(5));
assert_eq!(t.process(), process(3));
assert_eq!(t.priority(), prio(20));
assert_eq!(t.core_affinity(), Some(core(1)));
}
// --- Valid state transitions ---
#[test]
fn ready_to_running() {
let mut t = test_thread(0, 0);
t.set_running(core(0)).unwrap();
assert!(matches!(t.state(), ThreadState::Running(c) if *c == core(0)));
}
#[test]
fn running_to_blocked() {
let mut t = test_thread(0, 0);
t.set_running(core(0)).unwrap();
t.block(BlockReason::WaitingAny).unwrap();
assert!(matches!(t.state(), ThreadState::Blocked(BlockReason::WaitingAny)));
}
#[test]
fn blocked_to_ready() {
let mut t = test_thread(0, 0);
t.set_running(core(0)).unwrap();
t.block(BlockReason::WaitingAny).unwrap();
t.unblock().unwrap();
assert!(matches!(t.state(), ThreadState::Ready));
}
#[test]
fn running_to_ready_via_preempt() {
let mut t = test_thread(0, 0);
t.set_running(core(0)).unwrap();
t.preempt().unwrap();
assert!(matches!(t.state(), ThreadState::Ready));
}
#[test]
fn running_to_dead() {
let mut t = test_thread(0, 0);
t.set_running(core(0)).unwrap();
t.exit().unwrap();
assert!(matches!(t.state(), ThreadState::Dead));
}
// --- Invalid state transitions ---
#[test]
fn dead_cannot_transition() {
let mut t = test_thread(0, 0);
t.set_running(core(0)).unwrap();
t.exit().unwrap();
assert!(t.set_running(core(0)).is_err());
assert!(t.block(BlockReason::WaitingAny).is_err());
assert!(t.unblock().is_err());
assert!(t.preempt().is_err());
assert!(t.exit().is_err());
}
#[test]
fn ready_cannot_block() {
let mut t = test_thread(0, 0);
assert!(t.block(BlockReason::WaitingAny).is_err());
}
#[test]
fn blocked_cannot_run_directly() {
let mut t = test_thread(0, 0);
t.set_running(core(0)).unwrap();
t.block(BlockReason::WaitingAny).unwrap();
assert!(t.set_running(core(0)).is_err());
}
#[test]
fn ready_cannot_preempt() {
let mut t = test_thread(0, 0);
assert!(t.preempt().is_err());
}
#[test]
fn ready_cannot_exit() {
let mut t = test_thread(0, 0);
assert!(t.exit().is_err());
}
#[test]
fn blocked_cannot_preempt() {
let mut t = test_thread(0, 0);
t.set_running(core(0)).unwrap();
t.block(BlockReason::WaitingAny).unwrap();
assert!(t.preempt().is_err());
}
// --- Time slice ---
#[test]
fn time_slice_accessible() {
let t = test_thread(0, 0);
let _ = t.time_slice();
}
#[test]
fn time_slice_mut_ticks() {
let mut t = test_thread(0, 0);
let expired = t.time_slice_mut().tick();
assert!(!expired);
}