mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-01-30 17:11:47 -05:00
syscalls: sleep: make interruptable()
Allow the `nanosleep` family of functions to be interrupted. When an interruption occures, calculation the remaining duration and write that back to user-space. Add a test to ensure proper functionality into usertest.
This commit is contained in:
@@ -1,23 +1,35 @@
|
||||
use libkernel::{error::Result, memory::address::TUA};
|
||||
use super::thread_group::signal::{InterruptResult, Interruptable};
|
||||
use crate::{
|
||||
clock::timespec::TimeSpec,
|
||||
drivers::timer::{now, sleep},
|
||||
memory::uaccess::copy_to_user,
|
||||
};
|
||||
use core::time::Duration;
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
memory::address::TUA,
|
||||
};
|
||||
|
||||
use crate::{clock::timespec::TimeSpec, drivers::timer::sleep};
|
||||
pub async fn sys_nanosleep(rqtp: TUA<TimeSpec>, rmtp: TUA<TimeSpec>) -> Result<usize> {
|
||||
let timespec: Duration = TimeSpec::copy_from_user(rqtp).await?.into();
|
||||
let started_at = now().unwrap();
|
||||
|
||||
pub async fn sys_nanosleep(rqtp: TUA<TimeSpec>, _rmtp: TUA<TimeSpec>) -> Result<usize> {
|
||||
let timespec = TimeSpec::copy_from_user(rqtp).await?;
|
||||
|
||||
sleep(timespec.into()).await;
|
||||
|
||||
Ok(0)
|
||||
match sleep(timespec).interruptable().await {
|
||||
InterruptResult::Interrupted => {
|
||||
if !rmtp.is_null() {
|
||||
let elapsed = now().unwrap() - started_at;
|
||||
copy_to_user(rmtp, (timespec - elapsed).into()).await?;
|
||||
}
|
||||
Err(KernelError::Interrupted)
|
||||
}
|
||||
InterruptResult::Uninterrupted(()) => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sys_clock_nanosleep(
|
||||
_clock_id: i32,
|
||||
rqtp: TUA<TimeSpec>,
|
||||
_rmtp: TUA<TimeSpec>,
|
||||
rmtp: TUA<TimeSpec>,
|
||||
) -> Result<usize> {
|
||||
let timespec = TimeSpec::copy_from_user(rqtp).await?;
|
||||
|
||||
sleep(timespec.into()).await;
|
||||
|
||||
Ok(0)
|
||||
sys_nanosleep(rqtp, rmtp).await
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ use std::{
|
||||
};
|
||||
|
||||
use futex_bitset::test_futex_bitset;
|
||||
use signals::test_interruptible_nanosleep;
|
||||
|
||||
mod futex_bitset;
|
||||
mod signals;
|
||||
|
||||
fn test_sync() {
|
||||
print!("Testing sync syscall ...");
|
||||
@@ -820,6 +822,7 @@ fn main() {
|
||||
run_test(test_rust_mutex);
|
||||
run_test(test_parking_lot_mutex_timeout);
|
||||
run_test(test_thread_with_name);
|
||||
run_test(test_interruptible_nanosleep);
|
||||
let end = std::time::Instant::now();
|
||||
println!("All tests passed in {} ms", (end - start).as_millis());
|
||||
}
|
||||
|
||||
74
usertest/src/signals.rs
Normal file
74
usertest/src/signals.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use std::{
|
||||
ptr,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
static SIGNAL_CAUGHT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
extern "C" fn signal_handler(_: libc::c_int) {
|
||||
SIGNAL_CAUGHT.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn register_handler(signum: libc::c_int, restart: bool) {
|
||||
unsafe {
|
||||
SIGNAL_CAUGHT.store(false, Ordering::Relaxed);
|
||||
let mut action: libc::sigaction = std::mem::zeroed();
|
||||
|
||||
action.sa_sigaction = signal_handler as *const () as usize;
|
||||
|
||||
action.sa_flags = if restart { libc::SA_RESTART } else { 0 };
|
||||
|
||||
libc::sigemptyset(&mut action.sa_mask);
|
||||
|
||||
if libc::sigaction(signum, &action, std::ptr::null_mut()) != 0 {
|
||||
panic!("Failed to register signal handler");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_interruptible_nanosleep() {
|
||||
print!("Testing interruptible nanosleep (EINTR) ...");
|
||||
|
||||
register_handler(libc::SIGALRM, false);
|
||||
|
||||
unsafe {
|
||||
let pid = libc::getpid();
|
||||
|
||||
if libc::fork() == 0 {
|
||||
// in child.
|
||||
let req = libc::timespec {
|
||||
tv_sec: 1,
|
||||
tv_nsec: 0,
|
||||
};
|
||||
|
||||
libc::nanosleep(&req, ptr::null_mut());
|
||||
libc::kill(pid, libc::SIGALRM);
|
||||
libc::exit(0);
|
||||
};
|
||||
|
||||
// Sleep for 5 seconds (much longer than the alarm)
|
||||
let req = libc::timespec {
|
||||
tv_sec: 5,
|
||||
tv_nsec: 0,
|
||||
};
|
||||
let mut rem = libc::timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
};
|
||||
|
||||
let ret = libc::nanosleep(&req, &mut rem);
|
||||
let err = std::io::Error::last_os_error();
|
||||
|
||||
// Nanosleep should return -1
|
||||
assert_eq!(ret, -1);
|
||||
|
||||
// Errno should be EINTR
|
||||
assert_eq!(err.raw_os_error(), Some(libc::EINTR));
|
||||
|
||||
// The signal handler should have run
|
||||
assert!(SIGNAL_CAUGHT.load(Ordering::Relaxed));
|
||||
|
||||
// The remaining time should be updated (approx 4 seconds left)
|
||||
assert!(rem.tv_sec >= 3 && rem.tv_sec <= 5);
|
||||
}
|
||||
println!(" OK");
|
||||
}
|
||||
Reference in New Issue
Block a user