From c4f1d9acf59a7cb1e9762c382bf24fde67472971 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Tue, 23 Dec 2025 11:25:36 -0800 Subject: [PATCH 1/2] support timeouts for futex --- Cargo.lock | 13 +++++++++++ Cargo.toml | 2 +- libkernel/src/error.rs | 3 +++ libkernel/src/error/syscall_error.rs | 2 ++ src/arch/arm64/exceptions/syscall.rs | 2 +- src/process/threading/futex/mod.rs | 35 +++++++++++++++++++++++++--- usertest/Cargo.toml | 1 + usertest/src/main.rs | 19 +++++++++++++++ 8 files changed, 72 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7786f67..8a0b610 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -125,6 +136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", "futures-sink", "futures-task", "pin-project-lite", @@ -530,6 +542,7 @@ name = "usertest" version = "0.1.0" dependencies = [ "libc", + "parking_lot", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index cf3bffc..b6f8289 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ async-trait = "0.1.88" getargs = { version = "0.5.0", default-features = false } ringbuf = { version = "0.4.8", default-features = false, features = ["alloc"] } bitflags = "2.9.1" -futures = { version = "0.3.31", default-features = false, features = ["alloc"] } +futures = { version = "0.3.31", default-features = false, features = ["alloc", "async-await"] } rand = { version = "0.9.2", default-features = false, features = ["small_rng"] } [profile.release] diff --git a/libkernel/src/error.rs b/libkernel/src/error.rs index d050a55..9dc0d3d 100644 --- a/libkernel/src/error.rs +++ b/libkernel/src/error.rs @@ -168,6 +168,9 @@ pub enum KernelError { #[error("No such process")] NoProcess, + #[error("Operation timed out")] + TimedOut, + #[error("{0}")] Other(&'static str), } diff --git a/libkernel/src/error/syscall_error.rs b/libkernel/src/error/syscall_error.rs index ab41c83..78b0152 100644 --- a/libkernel/src/error/syscall_error.rs +++ b/libkernel/src/error/syscall_error.rs @@ -38,6 +38,7 @@ pub const EDOM: isize = -33; pub const ERANGE: isize = -34; pub const EWOULDBLOCK: isize = -EAGAIN; pub const ENOSYS: isize = -38; +pub const ETIMEDOUT: isize = -110; pub fn kern_err_to_syscall(err: KernelError) -> isize { match err { @@ -51,6 +52,7 @@ pub fn kern_err_to_syscall(err: KernelError) -> isize { KernelError::SeekPipe => ESPIPE, KernelError::NotSupported => ENOSYS, KernelError::NoMemory => ENOMEM, + KernelError::TimedOut => ETIMEDOUT, e => todo!("{e}"), } } diff --git a/src/arch/arm64/exceptions/syscall.rs b/src/arch/arm64/exceptions/syscall.rs index a784e18..157ddc1 100644 --- a/src/arch/arm64/exceptions/syscall.rs +++ b/src/arch/arm64/exceptions/syscall.rs @@ -173,7 +173,7 @@ pub async fn handle_syscall() { TUA::from_value(arg1 as _), arg2 as _, arg3 as _, - VA::from_value(arg4 as _), + TUA::from_value(arg4 as _), TUA::from_value(arg5 as _), arg6 as _, ) diff --git a/src/process/threading/futex/mod.rs b/src/process/threading/futex/mod.rs index 4febfa6..3e43a02 100644 --- a/src/process/threading/futex/mod.rs +++ b/src/process/threading/futex/mod.rs @@ -1,9 +1,15 @@ +use crate::clock::realtime::date; +use crate::clock::timespec::TimeSpec; +use crate::drivers::timer::sleep; use crate::sync::{OnceLock, SpinLock}; +use alloc::boxed::Box; use alloc::{collections::btree_map::BTreeMap, sync::Arc}; +use core::time::Duration; +use futures::FutureExt; use key::FutexKey; use libkernel::{ error::{KernelError, Result}, - memory::address::{TUA, VA}, + memory::address::TUA, sync::waker_set::WakerSet, }; use wait::FutexWait; @@ -60,7 +66,7 @@ pub async fn sys_futex( uaddr: TUA, op: i32, val: u32, - _timeout: VA, + timeout: TUA, _uaddr2: TUA, _val3: u32, ) -> Result { @@ -74,13 +80,36 @@ pub async fn sys_futex( }; // TODO: support bitset variants properly + let timeout = if timeout.is_null() { + None + } else { + let timeout = TimeSpec::copy_from_user(timeout).await?; + if matches!(cmd, FUTEX_WAIT_BITSET) { + Some(Duration::from(timeout) - date()) + } else { + Some(Duration::from(timeout)) + } + }; match cmd { FUTEX_WAIT | FUTEX_WAIT_BITSET => { // Obtain (or create) the wait-queue for this futex word. let slot = get_or_create_queue(key); // Return 0 on success. - FutexWait::new(uaddr, val, slot).await.map(|_| 0) + if let Some(dur) = timeout { + let mut wait = FutexWait::new(uaddr, val, slot).fuse(); + let mut sleep = Box::pin(sleep(dur).fuse()); + futures::select_biased! { + res = wait => { + res.map(|_| 0) + }, + _ = sleep => { + Err(KernelError::TimedOut) + } + } + } else { + FutexWait::new(uaddr, val, slot).await.map(|_| 0) + } } FUTEX_WAKE | FUTEX_WAKE_BITSET => Ok(wake_key(val as _, key)), diff --git a/usertest/Cargo.toml b/usertest/Cargo.toml index 21f8c55..7dd7759 100644 --- a/usertest/Cargo.toml +++ b/usertest/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] libc = "0.2" +parking_lot = "0.12" diff --git a/usertest/src/main.rs b/usertest/src/main.rs index d86a94c..6b95e36 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -291,6 +291,24 @@ fn test_rust_mutex() { println!(" OK"); } +fn test_parking_lot_mutex_timeout() { + print!("Testing parking_lot mutex with timeout ..."); + use parking_lot::Mutex; + use std::time::Duration; + let mtx = Arc::new(Mutex::new(())); + let mtx_clone = Arc::clone(&mtx); + let guard = mtx.lock(); + // Now try to acquire the lock with a timeout in another thread + let handle = thread::spawn(move || { + let timeout = Duration::from_millis(100); + let result = mtx_clone.try_lock_for(timeout); + assert!(result.is_none(), "Expected to not acquire the lock"); + }); + handle.join().unwrap(); + drop(guard); + println!(" OK"); +} + fn run_test(test_fn: fn()) { // Fork a new process to run the test unsafe { @@ -329,6 +347,7 @@ fn main() { run_test(test_rust_dir); run_test(test_rust_thread); run_test(test_rust_mutex); + run_test(test_parking_lot_mutex_timeout); let end = std::time::Instant::now(); println!("All tests passed in {} ms", (end - start).as_millis()); } From 1d25f0d292d898f82a7e7b8b9fc992211f4c4c90 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Wed, 24 Dec 2025 00:02:29 -0800 Subject: [PATCH 2/2] apply fix --- src/clock/realtime.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clock/realtime.rs b/src/clock/realtime.rs index 05f8a44..712210d 100644 --- a/src/clock/realtime.rs +++ b/src/clock/realtime.rs @@ -1,5 +1,5 @@ use crate::{ - drivers::timer::{Instant, now}, + drivers::timer::{Instant, now, uptime}, sync::SpinLock, }; use core::time::Duration; @@ -14,7 +14,7 @@ pub fn date() -> Duration { let duraton_since_ep_info = now - ep_info.1; ep_info.0 + duraton_since_ep_info } else { - Duration::new(0, 0) + uptime() } }