From 2d17177cb5ddcd75962f8d2b2c55778383d0aee3 Mon Sep 17 00:00:00 2001 From: Chen Chengjun Date: Tue, 30 Apr 2024 11:23:19 +0800 Subject: [PATCH] Re-implement the alarm syscall --- .../src/process/posix_thread/builder.rs | 22 +--- .../aster-nix/src/process/posix_thread/mod.rs | 11 -- .../src/process/posix_thread/timer.rs | 111 ------------------ .../aster-nix/src/process/process/builder.rs | 4 +- kernel/aster-nix/src/process/process/mod.rs | 91 ++++++++++---- kernel/aster-nix/src/syscall/alarm.rs | 19 +-- 6 files changed, 82 insertions(+), 176 deletions(-) delete mode 100644 kernel/aster-nix/src/process/posix_thread/timer.rs diff --git a/kernel/aster-nix/src/process/posix_thread/builder.rs b/kernel/aster-nix/src/process/posix_thread/builder.rs index 289e50eec..5ed3db918 100644 --- a/kernel/aster-nix/src/process/posix_thread/builder.rs +++ b/kernel/aster-nix/src/process/posix_thread/builder.rs @@ -2,15 +2,12 @@ use aster_frame::user::UserSpace; -use super::{PosixThread, PosixThreadExt, RealTimer}; +use super::PosixThread; use crate::{ prelude::*, process::{ posix_thread::name::ThreadName, - signal::{ - constants::SIGALRM, sig_mask::SigMask, sig_queues::SigQueues, - signals::kernel::KernelSignal, - }, + signal::{sig_mask::SigMask, sig_queues::SigQueues}, Credentials, Process, }, thread::{status::ThreadStatus, task, thread_table, Thread, Tid}, @@ -94,20 +91,6 @@ impl PosixThreadBuilder { is_main_thread, } = self; - let real_timer = RealTimer::new(move || { - let process = { - let Some(current_thread) = thread_table::get_thread(tid) else { - return; - }; - let posix_thread = current_thread.as_posix_thread().unwrap(); - posix_thread.process() - }; - - let signal = KernelSignal::new(SIGALRM); - process.enqueue_signal(signal); - }) - .unwrap(); - let thread = Arc::new_cyclic(|thread_ref| { let task = task::create_new_user_task(user_space, thread_ref.clone()); let status = ThreadStatus::Init; @@ -118,7 +101,6 @@ impl PosixThreadBuilder { set_child_tid: Mutex::new(set_child_tid), clear_child_tid: Mutex::new(clear_child_tid), credentials, - real_timer: Mutex::new(real_timer), sig_mask: Mutex::new(sig_mask), sig_queues, sig_context: Mutex::new(None), diff --git a/kernel/aster-nix/src/process/posix_thread/mod.rs b/kernel/aster-nix/src/process/posix_thread/mod.rs index 142a4f961..2a529223b 100644 --- a/kernel/aster-nix/src/process/posix_thread/mod.rs +++ b/kernel/aster-nix/src/process/posix_thread/mod.rs @@ -26,13 +26,11 @@ pub mod futex; mod name; mod posix_thread_ext; mod robust_list; -mod timer; pub use builder::PosixThreadBuilder; pub use name::{ThreadName, MAX_THREAD_NAME_LEN}; pub use posix_thread_ext::PosixThreadExt; pub use robust_list::RobustListHead; -pub use timer::RealTimer; pub struct PosixThread { // Immutable part @@ -52,9 +50,6 @@ pub struct PosixThread { /// Process credentials. At the kernel level, credentials are a per-thread attribute. credentials: Credentials, - /// The timer counts down in real (i.e., wall clock) time - real_timer: Mutex, - // Signal /// Blocked signals sig_mask: Mutex, @@ -146,10 +141,6 @@ impl PosixThread { return_errno_with_message!(Errno::EPERM, "sending signal to the thread is not allowed."); } - pub fn real_timer(&self) -> &Mutex { - &self.real_timer - } - pub(in crate::process) fn enqueue_signal(&self, signal: Box) { self.sig_queues.enqueue(signal); } @@ -242,8 +233,6 @@ impl PosixThread { thread_table::remove_thread(tid); } - self.real_timer.lock().clear(); - if self.is_main_thread() || self.is_last_thread() { // exit current process. debug!("self is main thread or last thread"); diff --git a/kernel/aster-nix/src/process/posix_thread/timer.rs b/kernel/aster-nix/src/process/posix_thread/timer.rs deleted file mode 100644 index 324c1d122..000000000 --- a/kernel/aster-nix/src/process/posix_thread/timer.rs +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -use core::time::Duration; - -use crate::{ - prelude::*, - thread::work_queue::{submit_work_item, work_item::WorkItem, WorkPriority}, - time::SystemTime, -}; - -/// A timer that counts down in real (wall clock) time to run delayed callbacks in process context. -/// -/// Unlike the `Timer` in `aster-frame`, the callbacks of this `RealTimer` will be executed in process -/// context instead of interrupt context. This leads to two differences: -/// -/// 1. The callbacks of this `RealTimer` can sleep, whereas the callbacks of `Timer` in `aster-frame` cannot. -/// 2. The callbacks of this `RealTimer` may be delayed by an arbitrary amount of time due to scheduler strategy. -/// -/// Note that the callbacks may not be executed in the process context of the timer's creator, so macros such -/// as `current` and `current_thread` should **NOT** be used in the callback. -/// -/// # Example -/// ```rust -/// let current_tid = current_thread!().tid(); -/// let timer = RealTimer::new(move || { -/// let current_thread = thread_table::get_thread(current_tid); -/// let posix_thread = current_thread.as_posix_thread().unwrap(); -/// let process = posix_thread.process(); -/// println!("Task executed for PID: {}", process.pid()); -/// }, Duration::from_secs(1)); -pub struct RealTimer { - timer: Arc, - expired_time: Option, -} - -impl RealTimer { - /// Creates a new `RealTimer`. The `callback` parameter will be called once the timeout is reached. - pub fn new(callback: impl Fn() + Send + Sync + Copy + 'static) -> Result { - let timer = { - aster_frame::timer::Timer::new(move |timer| { - let work_func = Box::new(callback); - let work_item = { Arc::new(WorkItem::new(work_func)) }; - // FIXME: set a higher priority like `WorkPriority::Alarm`. - submit_work_item(work_item, WorkPriority::High); - })? - }; - - Ok(Self { - timer, - expired_time: None, - }) - } - - /// Sets a new timeout value. If the old timeout is already set, the timeout will be refreshed. - pub fn set(&mut self, timeout: Duration) -> Result<()> { - assert_ne!(timeout, ZERO_DURATION); - - let new_expired_time = { - let now = SystemTime::now(); - now.checked_add(timeout) - .ok_or_else(|| Error::with_message(Errno::EINVAL, "Invalid duration"))? - }; - - self.expired_time = Some(new_expired_time); - - self.timer.set(timeout); - - Ok(()) - } - - /// Returns the remaining time until the task is executed. If the `timer` is expired or cleared, - /// this method will return zero. - pub fn remain(&self) -> Duration { - let Some(expired_time) = &self.expired_time else { - return ZERO_DURATION; - }; - - let now = SystemTime::now(); - match expired_time.duration_since(&now) { - Ok(duration) => duration, - Err(_) => ZERO_DURATION, - } - } - - /// Returns whether the timer has expired. - pub fn is_expired(&self) -> bool { - self.remain() == ZERO_DURATION - } - - /// Clears the timer. - pub fn clear(&mut self) { - self.timer.clear(); - self.expired_time = None; - } -} - -impl Drop for RealTimer { - fn drop(&mut self) { - self.timer.clear(); - } -} - -impl PartialEq for RealTimer { - fn eq(&self, other: &Self) -> bool { - self.expired_time == other.expired_time - } -} - -impl Eq for RealTimer {} - -const ZERO_DURATION: Duration = Duration::new(0, 0); diff --git a/kernel/aster-nix/src/process/process/builder.rs b/kernel/aster-nix/src/process/process/builder.rs index 3408e7e34..baa3090a7 100644 --- a/kernel/aster-nix/src/process/process/builder.rs +++ b/kernel/aster-nix/src/process/process/builder.rs @@ -173,7 +173,7 @@ impl<'a> ProcessBuilder<'a> { let process = { let threads = Vec::new(); - Arc::new(Process::new( + Process::new( pid, parent, threads, @@ -185,7 +185,7 @@ impl<'a> ProcessBuilder<'a> { sig_dispositions, resource_limits, nice, - )) + ) }; let thread = if let Some(thread_builder) = main_thread_builder { diff --git a/kernel/aster-nix/src/process/process/mod.rs b/kernel/aster-nix/src/process/process/mod.rs index a45fac948..f907fe4c0 100644 --- a/kernel/aster-nix/src/process/process/mod.rs +++ b/kernel/aster-nix/src/process/process/mod.rs @@ -6,7 +6,10 @@ use super::{ process_vm::{Heap, InitStackReader, ProcessVm}, rlimit::ResourceLimits, signal::{ - constants::SIGCHLD, sig_disposition::SigDispositions, sig_mask::SigMask, signals::Signal, + constants::{SIGALRM, SIGCHLD}, + sig_disposition::SigDispositions, + sig_mask::SigMask, + signals::{kernel::KernelSignal, Signal}, Pauser, }, status::ProcessStatus, @@ -17,7 +20,12 @@ use crate::{ fs::{file_table::FileTable, fs_resolver::FsResolver, utils::FileCreationMask}, prelude::*, sched::nice::Nice, - thread::{allocate_tid, Thread}, + thread::{ + allocate_tid, + work_queue::{submit_work_item, work_item::WorkItem}, + Thread, + }, + time::{clocks::RealTimeClock, Timer}, vm::vmar::Vmar, }; @@ -52,7 +60,8 @@ pub struct Process { process_vm: ProcessVm, /// Wait for child status changed children_pauser: Arc, - + /// The timer counts down in real (i.e., wall clock) time + alarm_timer: Arc, // Mutable Part /// The executable path. executable_path: RwLock, @@ -84,6 +93,26 @@ pub struct Process { sig_dispositions: Arc>, } +fn create_process_timer_callback(process_ref: &Weak) -> impl Fn() { + let current_process = process_ref.clone(); + let sent_signal = move || { + let signal = KernelSignal::new(SIGALRM); + if let Some(process) = current_process.upgrade() { + process.enqueue_signal(signal); + } + }; + + let work_func = Box::new(sent_signal); + let work_item = Arc::new(WorkItem::new(work_func)); + + move || { + submit_work_item( + work_item.clone(), + crate::thread::work_queue::WorkPriority::High, + ); + } +} + impl Process { #[allow(clippy::too_many_arguments)] fn new( @@ -98,7 +127,7 @@ impl Process { sig_dispositions: Arc>, resource_limits: ResourceLimits, nice: Nice, - ) -> Self { + ) -> Arc { let children_pauser = { // SIGCHID does not interrupt pauser. Child process will // resume paused parent when doing exit. @@ -106,23 +135,29 @@ impl Process { Pauser::new_with_mask(sigmask) }; - Self { - pid, - threads: Mutex::new(threads), - executable_path: RwLock::new(executable_path), - process_vm, - children_pauser, - status: Mutex::new(ProcessStatus::Uninit), - parent: Mutex::new(parent), - children: Mutex::new(BTreeMap::new()), - process_group: Mutex::new(Weak::new()), - file_table, - fs, - umask, - sig_dispositions, - resource_limits: Mutex::new(resource_limits), - nice: Atomic::new(nice), - } + Arc::new_cyclic(|process_ref: &Weak| { + let callback = create_process_timer_callback(process_ref); + let alarm_timer = RealTimeClock::timer_manager().create_timer(callback); + + Self { + pid, + threads: Mutex::new(threads), + executable_path: RwLock::new(executable_path), + process_vm, + children_pauser, + alarm_timer, + status: Mutex::new(ProcessStatus::Uninit), + parent: Mutex::new(parent), + children: Mutex::new(BTreeMap::new()), + process_group: Mutex::new(Weak::new()), + file_table, + fs, + umask, + sig_dispositions, + resource_limits: Mutex::new(resource_limits), + nice: Atomic::new(nice), + } + }) } /// init a user process and run the process @@ -198,6 +233,10 @@ impl Process { self.pid } + pub fn alarm_timer(&self) -> &Arc { + &self.alarm_timer + } + pub fn threads(&self) -> &Mutex>> { &self.threads } @@ -597,6 +636,9 @@ pub fn current() -> Arc { #[cfg(ktest)] mod test { + + use spin::Once; + use super::*; fn new_process(parent: Option>) -> Arc { @@ -608,7 +650,7 @@ mod test { } else { Weak::new() }; - Arc::new(Process::new( + Process::new( pid, parent, vec![], @@ -620,7 +662,7 @@ mod test { Arc::new(Mutex::new(SigDispositions::default())), ResourceLimits::default(), Nice::default(), - )) + ) } fn new_process_in_session(parent: Option>) -> Arc { @@ -660,6 +702,7 @@ mod test { #[ktest] fn init_process() { + crate::time::clocks::init_for_ktest(); let process = new_process(None); assert!(process.process_group().is_none()); assert!(process.session().is_none()); @@ -667,6 +710,7 @@ mod test { #[ktest] fn init_process_in_session() { + crate::time::clocks::init_for_ktest(); let process = new_process_in_session(None); assert!(process.is_group_leader()); assert!(process.is_session_leader()); @@ -675,6 +719,7 @@ mod test { #[ktest] fn to_new_session() { + crate::time::clocks::init_for_ktest(); let process = new_process_in_session(None); let sess = process.session().unwrap(); sess.inner.lock().leader = None; diff --git a/kernel/aster-nix/src/syscall/alarm.rs b/kernel/aster-nix/src/syscall/alarm.rs index 99977d09a..f6a66fe03 100644 --- a/kernel/aster-nix/src/syscall/alarm.rs +++ b/kernel/aster-nix/src/syscall/alarm.rs @@ -3,26 +3,27 @@ use core::time::Duration; use super::SyscallReturn; -use crate::{prelude::*, process::posix_thread::PosixThreadExt}; +use crate::prelude::*; pub fn sys_alarm(seconds: u32) -> Result { debug!("seconds = {}", seconds); - let current_thread = current_thread!(); - let mut real_timer = { - let posix_thread = current_thread.as_posix_thread().unwrap(); - posix_thread.real_timer().lock() - }; + let current = current!(); + let alarm_timer = current.alarm_timer(); - let remaining_secs = real_timer.remain().as_secs(); + let remaining = alarm_timer.remain(); + let mut remaining_secs = remaining.as_secs(); + if remaining.subsec_nanos() > 0 { + remaining_secs += 1; + } if seconds == 0 { // Clear previous timer - real_timer.clear(); + alarm_timer.cancel(); return Ok(SyscallReturn::Return(remaining_secs as _)); } - real_timer.set(Duration::from_secs(seconds as u64))?; + alarm_timer.set_timeout(Duration::from_secs(seconds as u64)); Ok(SyscallReturn::Return(remaining_secs as _)) }