fix: sched round-robin, cap slot OOB panic, clippy, make ci
- 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
This commit is contained in:
@@ -197,3 +197,18 @@ fn revoke_syscall_without_revoke_permission_fails() {
|
||||
SyscallOutcome::Complete(SyscallResult::Error(_))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_representable_slot_is_rejected_not_panicking() {
|
||||
let (mut k, _) = kernel_with_process();
|
||||
for raw in 1..=CapSlot::MAX {
|
||||
let outcome = k.handle_syscall(Syscall::Send {
|
||||
endpoint: CapSlot::new(raw).unwrap(),
|
||||
message: domain::message::Message::empty(),
|
||||
});
|
||||
assert!(
|
||||
matches!(outcome, SyscallOutcome::Complete(SyscallResult::Error(_))),
|
||||
"slot {raw} should be an error"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use crate::{SyscallOutcome, SyscallResult};
|
||||
use crate::SyscallOutcome;
|
||||
use domain::syscall::Syscall;
|
||||
|
||||
#[test]
|
||||
@@ -21,5 +21,5 @@ fn exit_thread_last_thread_destroys_process() {
|
||||
let (mut k, pid) = kernel_with_process();
|
||||
k.handle_syscall(Syscall::ExitThread);
|
||||
// Process should be gone since it had only one thread
|
||||
assert!(k.process_exists(pid) == false);
|
||||
assert!(!k.process_exists(pid));
|
||||
}
|
||||
|
||||
@@ -65,20 +65,20 @@ impl Kernel<MockPlatform> {
|
||||
if current == tid {
|
||||
return;
|
||||
}
|
||||
if let Ok(t) = self.threads.get_mut(current) {
|
||||
if matches!(t.state(), ThreadState::Running(_)) {
|
||||
let priority = t.priority();
|
||||
let _ = t.preempt();
|
||||
self.scheduler.add(current, priority, core);
|
||||
}
|
||||
if let Ok(t) = self.threads.get_mut(current)
|
||||
&& matches!(t.state(), ThreadState::Running(_))
|
||||
{
|
||||
let priority = t.priority();
|
||||
let _ = t.preempt();
|
||||
self.scheduler.add(current, priority, core);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(t) = self.threads.get(tid) {
|
||||
if matches!(t.state(), ThreadState::Ready) {
|
||||
self.scheduler.next(core);
|
||||
let _ = self.threads.get_mut(tid).map(|t| t.set_running(core));
|
||||
}
|
||||
if let Ok(t) = self.threads.get(tid)
|
||||
&& matches!(t.state(), ThreadState::Ready)
|
||||
{
|
||||
self.scheduler.next(core);
|
||||
let _ = self.threads.get_mut(tid).map(|t| t.set_running(core));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ fn send_without_receiver_blocks_caller() {
|
||||
|
||||
#[test]
|
||||
fn receive_without_sender_blocks_caller() {
|
||||
let (mut k, _, pid_b, _, cap_b, tid_b) = setup_ipc_kernel();
|
||||
let (mut k, _, _pid_b, _, cap_b, tid_b) = setup_ipc_kernel();
|
||||
|
||||
// Switch to thread B to call Receive
|
||||
k.switch_to_thread_for_test(tid_b);
|
||||
@@ -83,7 +83,7 @@ fn receive_without_sender_blocks_caller() {
|
||||
|
||||
#[test]
|
||||
fn send_with_waiting_receiver_delivers_and_switches() {
|
||||
let (mut k, pid_a, pid_b, cap_a, cap_b, tid_b) = setup_ipc_kernel();
|
||||
let (mut k, _pid_a, _pid_b, cap_a, cap_b, tid_b) = setup_ipc_kernel();
|
||||
let tid_a = ThreadId::from_raw(0);
|
||||
|
||||
// Thread B calls Receive first (blocks)
|
||||
@@ -108,8 +108,8 @@ fn send_with_waiting_receiver_delivers_and_switches() {
|
||||
|
||||
#[test]
|
||||
fn receive_with_waiting_sender_delivers_immediately() {
|
||||
let (mut k, pid_a, pid_b, cap_a, cap_b, tid_b) = setup_ipc_kernel();
|
||||
let tid_a = ThreadId::from_raw(0);
|
||||
let (mut k, _pid_a, _pid_b, cap_a, cap_b, tid_b) = setup_ipc_kernel();
|
||||
let _tid_a = ThreadId::from_raw(0);
|
||||
|
||||
// Thread A sends first (blocks)
|
||||
let msg = Message::from_words([99, 0, 0, 0]);
|
||||
@@ -184,7 +184,7 @@ fn ack_interrupt_returns_ok() {
|
||||
|
||||
#[test]
|
||||
fn wait_any_with_ready_source_returns_immediately() {
|
||||
let (mut k, pid_a, _, cap_a, cap_b, tid_b) = setup_ipc_kernel();
|
||||
let (mut k, _pid_a, _, cap_a, cap_b, tid_b) = setup_ipc_kernel();
|
||||
let tid_a = ThreadId::from_raw(0);
|
||||
|
||||
// Thread B sends a message (blocks since no receiver)
|
||||
@@ -206,7 +206,7 @@ fn wait_any_with_ready_source_returns_immediately() {
|
||||
|
||||
#[test]
|
||||
fn wait_any_with_no_ready_source_blocks() {
|
||||
let (mut k, pid_a, _, cap_a, _, _) = setup_ipc_kernel();
|
||||
let (mut k, _pid_a, _, cap_a, _, _) = setup_ipc_kernel();
|
||||
let wait_set = WaitSet::new([Some(cap_a), None, None, None], [None, None, None, None]);
|
||||
let outcome = k.handle_syscall(Syscall::WaitAny { sources: wait_set });
|
||||
assert!(matches!(
|
||||
|
||||
@@ -41,7 +41,7 @@ impl MockPlatform {
|
||||
impl Platform for MockPlatform {
|
||||
fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) {}
|
||||
fn enable_user_mode(&mut self, _entry: EntryPoint, _stack: StackPointer) -> ! {
|
||||
loop {}
|
||||
panic!("enable_user_mode is not supported in tests")
|
||||
}
|
||||
fn set_timer(&mut self, _duration: TimerDuration) {}
|
||||
fn current_ticks(&self) -> Ticks {
|
||||
@@ -65,16 +65,17 @@ impl Platform for MockPlatform {
|
||||
) {
|
||||
}
|
||||
fn halt(&self) -> ! {
|
||||
loop {}
|
||||
panic!("halt")
|
||||
}
|
||||
fn debug_putchar(&self, _byte: u8) {}
|
||||
|
||||
fn translate_va(&self, process: ProcessId, va: VirtualAddress) -> Option<PhysicalAddress> {
|
||||
for m in &self.mappings {
|
||||
if let Some(m) = m {
|
||||
if m.process.raw() == process.raw() && m.va == va.raw() {
|
||||
return Some(PhysicalAddress::from_raw(m.pa));
|
||||
}
|
||||
if let Some(m) = m
|
||||
&& m.process.raw() == process.raw()
|
||||
&& m.va == va.raw()
|
||||
{
|
||||
return Some(PhysicalAddress::from_raw(m.pa));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -12,16 +12,16 @@ fn tick_continues_when_time_remaining() {
|
||||
|
||||
#[test]
|
||||
fn tick_preempts_when_time_expired_and_other_thread_ready() {
|
||||
let mut k = test_kernel();
|
||||
let _k = test_kernel();
|
||||
let mock = MockPlatform::new(1);
|
||||
let pool = PagePool::new(PhysicalAddress::from_raw(0x8000_0000), 1024);
|
||||
let mut k = Kernel::new(mock, pool);
|
||||
|
||||
// Boot with quantum=2 so it expires after 2 ticks
|
||||
let (pid, tid1) = k.boot(Priority::DEFAULT, TimeSlice::new(2)).unwrap();
|
||||
let (pid, _tid1) = k.boot(Priority::DEFAULT, TimeSlice::new(2)).unwrap();
|
||||
|
||||
// Spawn a second thread so scheduler has someone to switch to
|
||||
let tid2 = k.spawn_thread_for_test(pid, Priority::DEFAULT, TimeSlice::DEFAULT);
|
||||
let _tid2 = k.spawn_thread_for_test(pid, Priority::DEFAULT, TimeSlice::DEFAULT);
|
||||
|
||||
// First tick: time remaining
|
||||
let d1 = k.handle_tick(CoreId::from_raw(0));
|
||||
|
||||
Reference in New Issue
Block a user