diff --git a/src/process/sleep.rs b/src/process/sleep.rs index 59964ba..1fb64c6 100644 --- a/src/process/sleep.rs +++ b/src/process/sleep.rs @@ -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, rmtp: TUA) -> Result { + let timespec: Duration = TimeSpec::copy_from_user(rqtp).await?.into(); + let started_at = now().unwrap(); -pub async fn sys_nanosleep(rqtp: TUA, _rmtp: TUA) -> Result { - 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, - _rmtp: TUA, + rmtp: TUA, ) -> Result { - let timespec = TimeSpec::copy_from_user(rqtp).await?; - - sleep(timespec.into()).await; - - Ok(0) + sys_nanosleep(rqtp, rmtp).await } diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 3ffeba0..d85afb5 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -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()); } diff --git a/usertest/src/signals.rs b/usertest/src/signals.rs new file mode 100644 index 0000000..43aabd7 --- /dev/null +++ b/usertest/src/signals.rs @@ -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"); +}