- RunList -> intrusive FIFO, no starvation, no silent drop - cap table sized to CapSlot range (kani caught OOB panic) - snake food color test uses const - kani cfg lint, RUST_MIN_STACK for big kernel struct in tests
312 lines
7.1 KiB
Rust
312 lines
7.1 KiB
Rust
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],
|
|
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<CoreId>,
|
|
) -> 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<CoreId>,
|
|
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<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)),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub const MAX_THREADS: usize = 256;
|
|
|
|
pub struct ThreadTable {
|
|
threads: [Option<Thread>; 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<CoreId>,
|
|
) -> Result<ThreadId, KernelError> {
|
|
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<CoreId>,
|
|
context: SavedContext,
|
|
) -> Result<ThreadId, KernelError> {
|
|
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<Thread, KernelError> {
|
|
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<Item = &Thread> {
|
|
self.threads
|
|
.iter()
|
|
.filter_map(|s| s.as_ref())
|
|
.filter(move |t| t.process() == process)
|
|
}
|
|
}
|
|
|
|
impl Default for ThreadTable {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|