Add syscall alarm

This commit is contained in:
Jianfeng Jiang
2023-12-12 07:00:40 +00:00
committed by Tate, Hongliang Tian
parent 286a3514b3
commit 3f402d2d88
8 changed files with 199 additions and 23 deletions

View File

@ -58,7 +58,7 @@ impl Timer {
///
/// 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();
match &lock.timer_callback {
Some(callback) => {

View File

@ -2,15 +2,18 @@
use aster_frame::user::UserSpace;
use super::PosixThread;
use super::{PosixThread, PosixThreadExt, RealTimer};
use crate::{
prelude::*,
process::{
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,
},
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
@ -90,8 +93,23 @@ impl PosixThreadBuilder {
sig_queues,
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 = 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 posix_thread = PosixThread {
process,
@ -100,6 +118,7 @@ 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: Mutex::new(sig_queues),
sig_context: Mutex::new(None),

View File

@ -26,11 +26,13 @@ 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
@ -50,10 +52,13 @@ pub struct PosixThread {
/// Process credentials. At the kernel level, credentials are a per-thread attribute.
credentials: Credentials,
// signal
/// blocked signals
/// The timer counts down in real (i.e., wall clock) time
real_timer: Mutex<RealTimer>,
// Signal
/// Blocked signals
sig_mask: Mutex<SigMask>,
/// thread-directed sigqueue
/// Thread-directed sigqueue
sig_queues: Mutex<SigQueues>,
/// 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.
@ -114,13 +119,13 @@ impl PosixThread {
let receiver_sid = self.process().session().unwrap().sid();
if receiver_sid == sender.sid() {
return Ok(());
} else {
}
return_errno_with_message!(
Errno::EPERM,
"sigcont requires that sender and receiver belongs to the same session"
);
}
}
let (receiver_ruid, receiver_suid) = {
let credentials = self.credentials();
@ -141,6 +146,10 @@ 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.lock().enqueue(signal);
}
@ -233,6 +242,8 @@ 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");

View 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);

View 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 _))
}

View File

@ -5,15 +5,16 @@
use aster_frame::cpu::UserContext;
use self::{
accept::sys_accept, bind::sys_bind, connect::sys_connect, execve::sys_execveat,
getgroups::sys_getgroups, getpeername::sys_getpeername, getrandom::sys_getrandom,
getresgid::sys_getresgid, getresuid::sys_getresuid, getsid::sys_getsid,
getsockname::sys_getsockname, getsockopt::sys_getsockopt, listen::sys_listen,
pread64::sys_pread64, recvfrom::sys_recvfrom, sendto::sys_sendto, setfsgid::sys_setfsgid,
setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups, setregid::sys_setregid,
setresgid::sys_setresgid, setresuid::sys_setresuid, setreuid::sys_setreuid, setsid::sys_setsid,
setsockopt::sys_setsockopt, setuid::sys_setuid, shutdown::sys_shutdown,
sigaltstack::sys_sigaltstack, socket::sys_socket, socketpair::sys_socketpair,
accept::sys_accept, alarm::sys_alarm, bind::sys_bind, connect::sys_connect,
execve::sys_execveat, getgroups::sys_getgroups, getpeername::sys_getpeername,
getrandom::sys_getrandom, getresgid::sys_getresgid, getresuid::sys_getresuid,
getsid::sys_getsid, getsockname::sys_getsockname, getsockopt::sys_getsockopt,
listen::sys_listen, pread64::sys_pread64, recvfrom::sys_recvfrom, sendto::sys_sendto,
setfsgid::sys_setfsgid, setfsuid::sys_setfsuid, setgid::sys_setgid, setgroups::sys_setgroups,
setregid::sys_setregid, setresgid::sys_setresgid, setresuid::sys_setresuid,
setreuid::sys_setreuid, setsid::sys_setsid, setsockopt::sys_setsockopt, setuid::sys_setuid,
shutdown::sys_shutdown, sigaltstack::sys_sigaltstack, socket::sys_socket,
socketpair::sys_socketpair,
};
use crate::{
prelude::*,
@ -96,6 +97,7 @@ use crate::{
mod accept;
mod access;
mod alarm;
mod arch_prctl;
mod bind;
mod brk;
@ -257,6 +259,7 @@ define_syscall_nums!(
SYS_DUP = 32,
SYS_DUP2 = 33,
SYS_PAUSE = 34,
SYS_ALARM = 37,
SYS_GETPID = 39,
SYS_SOCKET = 41,
SYS_CONNECT = 42,
@ -443,6 +446,7 @@ pub fn syscall_dispatch(
SYS_DUP => syscall_handler!(1, sys_dup, args),
SYS_DUP2 => syscall_handler!(2, sys_dup2, args),
SYS_PAUSE => syscall_handler!(0, sys_pause),
SYS_ALARM => syscall_handler!(1, sys_alarm, args),
SYS_GETPID => syscall_handler!(0, sys_getpid),
SYS_SOCKET => syscall_handler!(3, sys_socket, args),
SYS_CONNECT => syscall_handler!(3, sys_connect, args),

View File

@ -3,7 +3,7 @@
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 \
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)))
CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))

View File

@ -0,0 +1,2 @@
AlarmTest.UserModeSpinning
AlarmTest.Restart_NoRandomSave