mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 17:03:23 +00:00
Add syscall alarm
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
286a3514b3
commit
3f402d2d88
@ -58,7 +58,7 @@ impl Timer {
|
|||||||
///
|
///
|
||||||
/// If a timeout value is already set, the timeout value will be refreshed.
|
/// If a timeout value is already set, the timeout value will be refreshed.
|
||||||
///
|
///
|
||||||
pub fn set(self: Arc<Self>, timeout: Duration) {
|
pub fn set(self: &Arc<Self>, timeout: Duration) {
|
||||||
let mut lock = self.inner.lock_irq_disabled();
|
let mut lock = self.inner.lock_irq_disabled();
|
||||||
match &lock.timer_callback {
|
match &lock.timer_callback {
|
||||||
Some(callback) => {
|
Some(callback) => {
|
||||||
|
@ -2,15 +2,18 @@
|
|||||||
|
|
||||||
use aster_frame::user::UserSpace;
|
use aster_frame::user::UserSpace;
|
||||||
|
|
||||||
use super::PosixThread;
|
use super::{PosixThread, PosixThreadExt, RealTimer};
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::{
|
process::{
|
||||||
posix_thread::name::ThreadName,
|
posix_thread::name::ThreadName,
|
||||||
signal::{sig_mask::SigMask, sig_queues::SigQueues},
|
signal::{
|
||||||
|
constants::SIGALRM, sig_mask::SigMask, sig_queues::SigQueues,
|
||||||
|
signals::kernel::KernelSignal,
|
||||||
|
},
|
||||||
Credentials, Process,
|
Credentials, Process,
|
||||||
},
|
},
|
||||||
thread::{status::ThreadStatus, task::create_new_user_task, thread_table, Thread, Tid},
|
thread::{status::ThreadStatus, task, thread_table, Thread, Tid},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The builder to build a posix thread
|
/// The builder to build a posix thread
|
||||||
@ -90,8 +93,23 @@ impl PosixThreadBuilder {
|
|||||||
sig_queues,
|
sig_queues,
|
||||||
is_main_thread,
|
is_main_thread,
|
||||||
} = self;
|
} = 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 thread = Arc::new_cyclic(|thread_ref| {
|
||||||
let task = create_new_user_task(user_space, thread_ref.clone());
|
let task = task::create_new_user_task(user_space, thread_ref.clone());
|
||||||
let status = ThreadStatus::Init;
|
let status = ThreadStatus::Init;
|
||||||
let posix_thread = PosixThread {
|
let posix_thread = PosixThread {
|
||||||
process,
|
process,
|
||||||
@ -100,6 +118,7 @@ impl PosixThreadBuilder {
|
|||||||
set_child_tid: Mutex::new(set_child_tid),
|
set_child_tid: Mutex::new(set_child_tid),
|
||||||
clear_child_tid: Mutex::new(clear_child_tid),
|
clear_child_tid: Mutex::new(clear_child_tid),
|
||||||
credentials,
|
credentials,
|
||||||
|
real_timer: Mutex::new(real_timer),
|
||||||
sig_mask: Mutex::new(sig_mask),
|
sig_mask: Mutex::new(sig_mask),
|
||||||
sig_queues: Mutex::new(sig_queues),
|
sig_queues: Mutex::new(sig_queues),
|
||||||
sig_context: Mutex::new(None),
|
sig_context: Mutex::new(None),
|
||||||
|
@ -26,11 +26,13 @@ pub mod futex;
|
|||||||
mod name;
|
mod name;
|
||||||
mod posix_thread_ext;
|
mod posix_thread_ext;
|
||||||
mod robust_list;
|
mod robust_list;
|
||||||
|
mod timer;
|
||||||
|
|
||||||
pub use builder::PosixThreadBuilder;
|
pub use builder::PosixThreadBuilder;
|
||||||
pub use name::{ThreadName, MAX_THREAD_NAME_LEN};
|
pub use name::{ThreadName, MAX_THREAD_NAME_LEN};
|
||||||
pub use posix_thread_ext::PosixThreadExt;
|
pub use posix_thread_ext::PosixThreadExt;
|
||||||
pub use robust_list::RobustListHead;
|
pub use robust_list::RobustListHead;
|
||||||
|
pub use timer::RealTimer;
|
||||||
|
|
||||||
pub struct PosixThread {
|
pub struct PosixThread {
|
||||||
// Immutable part
|
// Immutable part
|
||||||
@ -50,10 +52,13 @@ pub struct PosixThread {
|
|||||||
/// Process credentials. At the kernel level, credentials are a per-thread attribute.
|
/// Process credentials. At the kernel level, credentials are a per-thread attribute.
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
|
|
||||||
// signal
|
/// The timer counts down in real (i.e., wall clock) time
|
||||||
/// blocked signals
|
real_timer: Mutex<RealTimer>,
|
||||||
|
|
||||||
|
// Signal
|
||||||
|
/// Blocked signals
|
||||||
sig_mask: Mutex<SigMask>,
|
sig_mask: Mutex<SigMask>,
|
||||||
/// thread-directed sigqueue
|
/// Thread-directed sigqueue
|
||||||
sig_queues: Mutex<SigQueues>,
|
sig_queues: Mutex<SigQueues>,
|
||||||
/// Signal handler ucontext address
|
/// Signal handler ucontext address
|
||||||
/// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp.
|
/// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp.
|
||||||
@ -114,12 +119,12 @@ impl PosixThread {
|
|||||||
let receiver_sid = self.process().session().unwrap().sid();
|
let receiver_sid = self.process().session().unwrap().sid();
|
||||||
if receiver_sid == sender.sid() {
|
if receiver_sid == sender.sid() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
|
||||||
return_errno_with_message!(
|
|
||||||
Errno::EPERM,
|
|
||||||
"sigcont requires that sender and receiver belongs to the same session"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EPERM,
|
||||||
|
"sigcont requires that sender and receiver belongs to the same session"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (receiver_ruid, receiver_suid) = {
|
let (receiver_ruid, receiver_suid) = {
|
||||||
@ -141,6 +146,10 @@ impl PosixThread {
|
|||||||
return_errno_with_message!(Errno::EPERM, "sending signal to the thread is not allowed.");
|
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>) {
|
pub(in crate::process) fn enqueue_signal(&self, signal: Box<dyn Signal>) {
|
||||||
self.sig_queues.lock().enqueue(signal);
|
self.sig_queues.lock().enqueue(signal);
|
||||||
}
|
}
|
||||||
@ -233,6 +242,8 @@ impl PosixThread {
|
|||||||
thread_table::remove_thread(tid);
|
thread_table::remove_thread(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.real_timer.lock().clear();
|
||||||
|
|
||||||
if self.is_main_thread() || self.is_last_thread() {
|
if self.is_main_thread() || self.is_last_thread() {
|
||||||
// exit current process.
|
// exit current process.
|
||||||
debug!("self is main thread or last thread");
|
debug!("self is main thread or last thread");
|
||||||
|
111
kernel/aster-nix/src/process/posix_thread/timer.rs
Normal file
111
kernel/aster-nix/src/process/posix_thread/timer.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// 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);
|
29
kernel/aster-nix/src/syscall/alarm.rs
Normal file
29
kernel/aster-nix/src/syscall/alarm.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
|
use super::{SyscallReturn, SYS_ALARM};
|
||||||
|
use crate::{log_syscall_entry, prelude::*, process::posix_thread::PosixThreadExt};
|
||||||
|
|
||||||
|
pub fn sys_alarm(seconds: u32) -> Result<SyscallReturn> {
|
||||||
|
log_syscall_entry!(SYS_ALARM);
|
||||||
|
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 remaining_secs = real_timer.remain().as_secs();
|
||||||
|
|
||||||
|
if seconds == 0 {
|
||||||
|
// Clear previous timer
|
||||||
|
real_timer.clear();
|
||||||
|
return Ok(SyscallReturn::Return(remaining_secs as _));
|
||||||
|
}
|
||||||
|
|
||||||
|
real_timer.set(Duration::from_secs(seconds as u64))?;
|
||||||
|
|
||||||
|
Ok(SyscallReturn::Return(remaining_secs as _))
|
||||||
|
}
|
@ -5,15 +5,16 @@
|
|||||||
use aster_frame::cpu::UserContext;
|
use aster_frame::cpu::UserContext;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
accept::sys_accept, bind::sys_bind, connect::sys_connect, execve::sys_execveat,
|
accept::sys_accept, alarm::sys_alarm, bind::sys_bind, connect::sys_connect,
|
||||||
getgroups::sys_getgroups, getpeername::sys_getpeername, getrandom::sys_getrandom,
|
execve::sys_execveat, getgroups::sys_getgroups, getpeername::sys_getpeername,
|
||||||
getresgid::sys_getresgid, getresuid::sys_getresuid, getsid::sys_getsid,
|
getrandom::sys_getrandom, getresgid::sys_getresgid, getresuid::sys_getresuid,
|
||||||
getsockname::sys_getsockname, getsockopt::sys_getsockopt, listen::sys_listen,
|
getsid::sys_getsid, getsockname::sys_getsockname, getsockopt::sys_getsockopt,
|
||||||
pread64::sys_pread64, recvfrom::sys_recvfrom, sendto::sys_sendto, setfsgid::sys_setfsgid,
|
listen::sys_listen, pread64::sys_pread64, recvfrom::sys_recvfrom, sendto::sys_sendto,
|
||||||
setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups, setregid::sys_setregid,
|
setfsgid::sys_setfsgid, setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups,
|
||||||
setresgid::sys_setresgid, setresuid::sys_setresuid, setreuid::sys_setreuid, setsid::sys_setsid,
|
setregid::sys_setregid, setresgid::sys_setresgid, setresuid::sys_setresuid,
|
||||||
setsockopt::sys_setsockopt, setuid::sys_setuid, shutdown::sys_shutdown,
|
setreuid::sys_setreuid, setsid::sys_setsid, setsockopt::sys_setsockopt, setuid::sys_setuid,
|
||||||
sigaltstack::sys_sigaltstack, socket::sys_socket, socketpair::sys_socketpair,
|
shutdown::sys_shutdown, sigaltstack::sys_sigaltstack, socket::sys_socket,
|
||||||
|
socketpair::sys_socketpair,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -96,6 +97,7 @@ use crate::{
|
|||||||
|
|
||||||
mod accept;
|
mod accept;
|
||||||
mod access;
|
mod access;
|
||||||
|
mod alarm;
|
||||||
mod arch_prctl;
|
mod arch_prctl;
|
||||||
mod bind;
|
mod bind;
|
||||||
mod brk;
|
mod brk;
|
||||||
@ -257,6 +259,7 @@ define_syscall_nums!(
|
|||||||
SYS_DUP = 32,
|
SYS_DUP = 32,
|
||||||
SYS_DUP2 = 33,
|
SYS_DUP2 = 33,
|
||||||
SYS_PAUSE = 34,
|
SYS_PAUSE = 34,
|
||||||
|
SYS_ALARM = 37,
|
||||||
SYS_GETPID = 39,
|
SYS_GETPID = 39,
|
||||||
SYS_SOCKET = 41,
|
SYS_SOCKET = 41,
|
||||||
SYS_CONNECT = 42,
|
SYS_CONNECT = 42,
|
||||||
@ -443,6 +446,7 @@ pub fn syscall_dispatch(
|
|||||||
SYS_DUP => syscall_handler!(1, sys_dup, args),
|
SYS_DUP => syscall_handler!(1, sys_dup, args),
|
||||||
SYS_DUP2 => syscall_handler!(2, sys_dup2, args),
|
SYS_DUP2 => syscall_handler!(2, sys_dup2, args),
|
||||||
SYS_PAUSE => syscall_handler!(0, sys_pause),
|
SYS_PAUSE => syscall_handler!(0, sys_pause),
|
||||||
|
SYS_ALARM => syscall_handler!(1, sys_alarm, args),
|
||||||
SYS_GETPID => syscall_handler!(0, sys_getpid),
|
SYS_GETPID => syscall_handler!(0, sys_getpid),
|
||||||
SYS_SOCKET => syscall_handler!(3, sys_socket, args),
|
SYS_SOCKET => syscall_handler!(3, sys_socket, args),
|
||||||
SYS_CONNECT => syscall_handler!(3, sys_connect, args),
|
SYS_CONNECT => syscall_handler!(3, sys_connect, args),
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
TESTS ?= chmod_test chown_test fsync_test getdents_test link_test lseek_test mkdir_test \
|
TESTS ?= chmod_test chown_test fsync_test getdents_test link_test lseek_test mkdir_test \
|
||||||
open_create_test open_test pty_test read_test rename_test stat_test \
|
open_create_test open_test pty_test read_test rename_test stat_test \
|
||||||
statfs_test symlink_test sync_test truncate_test uidgid_test unlink_test \
|
statfs_test symlink_test sync_test truncate_test uidgid_test unlink_test \
|
||||||
vdso_clock_gettime_test write_test
|
vdso_clock_gettime_test write_test alarm_test
|
||||||
|
|
||||||
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||||
CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
|
CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
|
||||||
|
2
regression/syscall_test/blocklists/alarm_test
Normal file
2
regression/syscall_test/blocklists/alarm_test
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
AlarmTest.UserModeSpinning
|
||||||
|
AlarmTest.Restart_NoRandomSave
|
Reference in New Issue
Block a user