mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 13:56:48 +00:00
Re-implement the alarm syscall
This commit is contained in:
parent
b226928349
commit
2d17177cb5
@ -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),
|
||||
|
@ -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<RealTimer>,
|
||||
|
||||
// Signal
|
||||
/// Blocked signals
|
||||
sig_mask: Mutex<SigMask>,
|
||||
@ -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<RealTimer> {
|
||||
&self.real_timer
|
||||
}
|
||||
|
||||
pub(in crate::process) fn enqueue_signal(&self, signal: Box<dyn Signal>) {
|
||||
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");
|
||||
|
@ -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<aster_frame::timer::Timer>,
|
||||
expired_time: Option<SystemTime>,
|
||||
}
|
||||
|
||||
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<Self> {
|
||||
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);
|
@ -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 {
|
||||
|
@ -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<Pauser>,
|
||||
|
||||
/// The timer counts down in real (i.e., wall clock) time
|
||||
alarm_timer: Arc<Timer>,
|
||||
// Mutable Part
|
||||
/// The executable path.
|
||||
executable_path: RwLock<String>,
|
||||
@ -84,6 +93,26 @@ pub struct Process {
|
||||
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
||||
}
|
||||
|
||||
fn create_process_timer_callback(process_ref: &Weak<Process>) -> 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<Mutex<SigDispositions>>,
|
||||
resource_limits: ResourceLimits,
|
||||
nice: Nice,
|
||||
) -> Self {
|
||||
) -> Arc<Self> {
|
||||
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<Process>| {
|
||||
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<Timer> {
|
||||
&self.alarm_timer
|
||||
}
|
||||
|
||||
pub fn threads(&self) -> &Mutex<Vec<Arc<Thread>>> {
|
||||
&self.threads
|
||||
}
|
||||
@ -597,6 +636,9 @@ pub fn current() -> Arc<Process> {
|
||||
|
||||
#[cfg(ktest)]
|
||||
mod test {
|
||||
|
||||
use spin::Once;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn new_process(parent: Option<Arc<Process>>) -> Arc<Process> {
|
||||
@ -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<Process>>) -> Arc<Process> {
|
||||
@ -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;
|
||||
|
@ -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<SyscallReturn> {
|
||||
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 _))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user