diff --git a/src/context/context.rs b/src/context/context.rs index cf5a0db98c..477f021028 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -16,7 +16,7 @@ use crate::{ memory::{allocate_p2frame, deallocate_p2frame, Enomem, Frame, RaiiFrame}, paging::{RmmA, RmmArch}, percpu::PercpuBlock, - scheme::FileHandle, + scheme::FileHandle, sync::WaitCondition, }; use crate::syscall::error::{Error, Result, EAGAIN, ESRCH}; @@ -43,9 +43,7 @@ pub enum Status { HardBlocked { reason: HardBlockedReason, }, - - Stopped(usize), - Exited(usize), + Exited { user_data: usize }, } impl Status { @@ -183,6 +181,7 @@ pub struct Context { pub userspace: bool, pub being_sigkilled: bool, pub fmap_ret: Option, + pub status_cond: Arc, } #[derive(Debug)] @@ -228,6 +227,7 @@ impl Context { userspace: false, fmap_ret: None, being_sigkilled: false, + status_cond: Arc::new(WaitCondition::new()), #[cfg(feature = "syscall_debug")] syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(), diff --git a/src/context/mod.rs b/src/context/mod.rs index df07976e6a..dfafaedb2a 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -85,6 +85,7 @@ pub fn init() { info: ProcessInfo::default(), waitpid: Arc::new(WaitMap::new()), threads: Vec::new(), + status: process::ProcessStatus::PossiblyRunnable, })) }); diff --git a/src/context/process.rs b/src/context/process.rs index fe63d07114..a6c01efd4c 100644 --- a/src/context/process.rs +++ b/src/context/process.rs @@ -29,6 +29,7 @@ pub struct Process { pub info: ProcessInfo, /// Context is being waited on pub waitpid: Arc>, + pub status: ProcessStatus, /// Threads of process pub threads: Vec>>, } @@ -69,6 +70,12 @@ impl DerefMut for Process { &mut self.info } } +#[derive(Debug)] +pub enum ProcessStatus { + PossiblyRunnable, + Stopped(usize), + Exited(usize), +} pub const INIT: ProcessId = ProcessId::new(1); static NEXT_PID: AtomicProcessId = AtomicProcessId::new(INIT); @@ -110,6 +117,7 @@ pub fn new_process(info: impl FnOnce(ProcessId) -> ProcessInfo) -> Result { + let user_data = buf.read_usize()?; + let mut context = context.write(); + + // TODO: Handle Status::HardBlocked differently? + context.status = Status::Exited { user_data }; + context.status_cond.notify(); + + Ok(mem::size_of::()) + } Self::OpenViaDup | Self::AwaitingAddrSpaceChange { .. } | Self::AwaitingFiletableChange { .. } => Err(Error::new(EBADF)), @@ -1520,6 +1532,26 @@ impl ContextHandle { buf.copy_exactly(crate::cpu_set::mask_as_bytes(&mask))?; Ok(mem::size_of_val(&mask)) } // TODO: Replace write() with SYS_DUP_FORWARD. + + ContextHandle::Status => { + // Writing to the status explicitly exits the current thread. + let cond = Arc::clone(&context.read().status_cond); + let user_data = loop { + let mut context = context.write(); + + if let Status::Exited { user_data } = context.status { + break user_data; + } + + if !cond.wait(context, "waiting for thread") { + return Err(Error::new(EINTR)); + } + }; + + buf.write_usize(user_data)?; + Ok(mem::size_of::()) + } + // TODO: Find a better way to switch address spaces, since they also require switching // the instruction and stack pointer. Maybe remove `/regs` altogether and replace it // with `/ctx` diff --git a/src/scheme/sys/context.rs b/src/scheme/sys/context.rs index 00172ab9e9..0a30d90a53 100644 --- a/src/scheme/sys/context.rs +++ b/src/scheme/sys/context.rs @@ -53,10 +53,7 @@ pub fn resource() -> Result> { stat_string.push('B'); } } - context::Status::Stopped(_sig) => { - stat_string.push('T'); - } - context::Status::Exited(_status) => { + context::Status::Exited { .. } => { stat_string.push('Z'); } } diff --git a/src/sync/wait_condition.rs b/src/sync/wait_condition.rs index cf5d58306a..cd87151bd8 100644 --- a/src/sync/wait_condition.rs +++ b/src/sync/wait_condition.rs @@ -44,7 +44,7 @@ impl WaitCondition { } // Wait until notified. Unlocks guard when blocking is ready. Returns false if resumed by a signal or the notify_signal function - pub fn wait(&self, guard: MutexGuard, reason: &'static str) -> bool { + pub fn wait(&self, guard: T, reason: &'static str) -> bool { let current_context_ref = context::current(); { { diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 667771f6b2..08a4cff251 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -1,4 +1,5 @@ use alloc::{sync::Arc, vec::Vec}; +use spinning_top::RwSpinlock; use core::{mem, num::NonZeroUsize, sync::atomic::Ordering}; use syscall::{sig_bit, SIGCHLD, SIGKILL, SIGSTOP, SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU}; @@ -6,9 +7,7 @@ use rmm::Arch; use spin::RwLock; use crate::context::{ - memory::{AddrSpace, Grant, PageSpan}, - process::{self, ProcessId, ProcessInfo}, - WaitpidKey, + memory::{AddrSpace, Grant, PageSpan}, process::{self, Process, ProcessId, ProcessInfo, ProcessStatus}, Context, WaitpidKey }; use crate::{ @@ -78,7 +77,7 @@ pub fn exit(status: usize) -> ! { } } - context_lock.write().status = context::Status::Exited(status); + process_lock.write().status = ProcessStatus::Exited(status); let children = process_lock.write().waitpid.receive_all(); @@ -174,11 +173,11 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { }, } - let mut send = |context: &mut context::Context, proc: &ProcessInfo| -> SendResult { - let is_self = context.is_current_context(); + let mut send = |context_lock: &Arc>, process_lock: &Arc>, proc_info: &ProcessInfo| -> SendResult { + let is_self = context::is_current(context_lock); // Non-root users cannot kill arbitrarily. - if euid != 0 && euid != proc.ruid && ruid != proc.ruid { + if euid != 0 && euid != proc_info.ruid && ruid != proc_info.ruid { return SendResult::Forbidden; } // If sig = 0, test that process exists and can be signalled, but don't send any @@ -187,15 +186,18 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { return SendResult::Succeeded; } + let mut process_guard = process_lock.write(); + if sig == SIGCONT - && let context::Status::Stopped(_sig) = context.status + && let ProcessStatus::Stopped(_sig) = process_guard.status { // Convert stopped processes to blocked if sending SIGCONT, regardless of whether // SIGCONT is blocked or ignored. It can however be controlled whether the process // will additionally ignore, defer, or handle that signal. - context.status = context::Status::Runnable; + process_guard.status = ProcessStatus::PossiblyRunnable; + drop(process_guard); - if let Some((tctl, pctl, _st)) = context.sigcontrol() { + if let Some((tctl, pctl, _st)) = context_lock.write().sigcontrol() { if !pctl.signal_will_ign(SIGCONT, false) { tctl.word[0].fetch_or(sig_bit(SIGCONT), Ordering::Relaxed); } @@ -205,39 +207,47 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { } } // POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs. - SendResult::SucceededSigcont { - ppid: proc.ppid, - pgid: proc.pgid, - } - } else if sig == SIGSTOP + return SendResult::SucceededSigcont { + ppid: proc_info.ppid, + pgid: proc_info.pgid, + }; + } + let mut context_guard = context_lock.write(); + if sig == SIGSTOP || (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP) - && context + && context_guard .sigcontrol() .map_or(false, |(_, proc, _)| proc.signal_will_stop(sig))) { - context.status = context::Status::Stopped(sig); + context_guard.status = context::Status::Blocked; // TODO: Actually wait for, or IPI the context first, then clear bit. Not atomically safe otherwise. - if let Some((ctl, _, _)) = context.sigcontrol() { + if let Some((ctl, _, _)) = context_guard.sigcontrol() { ctl.word[0].fetch_and(!sig_bit(SIGCONT), Ordering::Relaxed); } - SendResult::SucceededSigchld { - ppid: proc.ppid, - pgid: proc.pgid, + drop(context_guard); + process_lock.write().status = ProcessStatus::Stopped(sig); + return SendResult::SucceededSigchld { + ppid: proc_info.ppid, + pgid: proc_info.pgid, orig_signal: sig, - } - } else if sig == SIGKILL { - context.being_sigkilled = true; - context.unblock(); + }; + } + if sig == SIGKILL { + context_guard.being_sigkilled = true; + context_guard.unblock(); + drop(context_guard); + process_lock.write().status = ProcessStatus::Exited(SIGKILL); killed_self |= is_self; // exit() will signal the parent, rather than immediately in kill() - SendResult::Succeeded - } else if let Some((tctl, pctl, _st)) = context.sigcontrol() + return SendResult::Succeeded; + } + if let Some((tctl, pctl, _st)) = context_guard.sigcontrol() && !pctl.signal_will_ign(sig, parent_sigchld) { let _was_new = tctl.word[sig_group].fetch_or(sig_bit(sig), Ordering::Relaxed); if (tctl.word[sig_group].load(Ordering::Relaxed) >> 32) & sig_bit(sig) != 0 { - context.unblock(); + context_guard.unblock(); killed_self |= is_self; } SendResult::Succeeded @@ -313,8 +323,7 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { process.info, ) }; - let mut context = context_lock.write(); - let result = send(&mut *context, &info); + let result = send(&context_lock, process_lock, &info); handle_send(pid, result)?; } } else if pid.get() == 1_usize.wrapping_neg() { @@ -333,14 +342,11 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { ) }; - if info.pid.get() <= 2 { + if info.pid.get() <= 1 { continue; } found += 1; - let mut context = context_lock.write(); - - let result = send(&mut *context, &info); - drop(context); + let result = send(&context_lock, process_lock, &info); handle_send(*pid, result)?; } } else { @@ -370,9 +376,7 @@ pub fn kill(pid: ProcessId, sig: usize, parent_sigchld: bool) -> Result { } found += 1; - let mut context = context_lock.write(); - let result = send(&mut *context, &info); - drop(context); + let result = send(&context_lock, process_lock, &info); handle_send(*pid, result)?; } @@ -600,7 +604,7 @@ pub fn waitpid( } }; - if let Some(context::Status::Exited(status)) = hack_status { + if let Some(ProcessStatus::Exited(_status)) = hack_status { /*let _ = waitpid.receive_nonblock(&WaitpidKey { pid: Some(pid), pgid: None,