From 74b296a2bfc34de1c6dcd34f9372380d9d523e0b Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 18 Oct 2026 00:16:38 +0200 Subject: [PATCH] feat(kernel): add Exit and ExitThread syscalls with process cleanup --- crates/kernel/src/lib.rs | 4 +++ crates/kernel/src/syscall.rs | 50 +++++++++++++++++++++++++++++++++ crates/kernel/src/tests/exit.rs | 25 +++++++++++++++++ crates/kernel/src/tests/mod.rs | 1 + 4 files changed, 80 insertions(+) create mode 100644 crates/kernel/src/tests/exit.rs diff --git a/crates/kernel/src/lib.rs b/crates/kernel/src/lib.rs index dd29b91..ac7539a 100644 --- a/crates/kernel/src/lib.rs +++ b/crates/kernel/src/lib.rs @@ -125,6 +125,10 @@ impl Kernel

{ } } + pub fn process_exists(&self, pid: ProcessId) -> bool { + self.processes.get(pid).is_ok() + } + fn check_cap( &self, owner: ProcessId, diff --git a/crates/kernel/src/syscall.rs b/crates/kernel/src/syscall.rs index a1e2af2..ce18d93 100644 --- a/crates/kernel/src/syscall.rs +++ b/crates/kernel/src/syscall.rs @@ -14,6 +14,8 @@ impl Kernel

{ Syscall::GetTime => self.handle_get_time(), Syscall::GetProcessId => self.handle_get_process_id(pid), Syscall::Yield => self.handle_yield(tid), + Syscall::Exit => self.handle_exit(tid, pid), + Syscall::ExitThread => self.handle_exit_thread(tid, pid), _ => SyscallOutcome::Complete(SyscallResult::Error( domain::error::KernelError::WouldBlock, )), @@ -29,6 +31,54 @@ impl Kernel

{ SyscallOutcome::Complete(SyscallResult::Value(pid.raw() as usize)) } + fn handle_exit(&mut self, caller_tid: types::id::ThreadId, pid: types::id::ProcessId) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + // Kill all threads of this process + let thread_ids: [Option; 4] = { + match self.processes.get(pid) { + Ok(p) => { + let mut ids = [None; 4]; + for (i, tid) in p.thread_ids().enumerate() { + if i < 4 { + ids[i] = Some(tid); + } + } + ids + } + Err(_) => return SyscallOutcome::TerminateCaller, + } + }; + + for slot in &thread_ids { + if let Some(tid) = slot { + self.scheduler.remove(*tid); + let _ = self.threads.destroy(*tid); + } + } + + let _ = self.processes.destroy(pid); + self.process_memory[pid.raw() as usize] = None; + SyscallOutcome::TerminateCaller + } + + fn handle_exit_thread(&mut self, caller_tid: types::id::ThreadId, pid: types::id::ProcessId) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + self.scheduler.remove(caller_tid); + let _ = self.threads.destroy(caller_tid); + + if let Ok(process) = self.processes.get_mut(pid) { + process.remove_thread(caller_tid); + if process.thread_ids().count() == 0 { + let _ = self.processes.destroy(pid); + self.process_memory[pid.raw() as usize] = None; + } + } + + SyscallOutcome::TerminateCaller + } + fn handle_yield(&mut self, caller_tid: types::id::ThreadId) -> SyscallOutcome { use domain::scheduler::Scheduler; diff --git a/crates/kernel/src/tests/exit.rs b/crates/kernel/src/tests/exit.rs new file mode 100644 index 0000000..423d134 --- /dev/null +++ b/crates/kernel/src/tests/exit.rs @@ -0,0 +1,25 @@ +use super::*; +use crate::{SyscallOutcome, SyscallResult}; +use domain::syscall::Syscall; + +#[test] +fn exit_thread_terminates_caller() { + let (mut k, _) = kernel_with_process(); + let outcome = k.handle_syscall(Syscall::ExitThread); + assert!(matches!(outcome, SyscallOutcome::TerminateCaller)); +} + +#[test] +fn exit_terminates_process_and_all_threads() { + let (mut k, _) = kernel_with_process(); + let outcome = k.handle_syscall(Syscall::Exit); + assert!(matches!(outcome, SyscallOutcome::TerminateCaller)); +} + +#[test] +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); +} diff --git a/crates/kernel/src/tests/mod.rs b/crates/kernel/src/tests/mod.rs index 6aacb27..2a94df6 100644 --- a/crates/kernel/src/tests/mod.rs +++ b/crates/kernel/src/tests/mod.rs @@ -1,3 +1,4 @@ +mod exit; mod simple; use domain::memory::page_pool::PagePool;