From 70505ff4f85fa1401fc87ffa204bc5d96a9ec8ae Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Thu, 1 Aug 2024 03:17:49 +0000 Subject: [PATCH] Support futex wait timeout --- kernel/src/process/posix_thread/futex.rs | 61 ++++++++------ kernel/src/process/signal/pause.rs | 100 ++++++++++++++++++++--- kernel/src/syscall/futex.rs | 90 ++++++++++++++++---- kernel/src/syscall/nanosleep.rs | 38 +++++++-- kernel/src/time/core/timer.rs | 12 +++ kernel/src/time/wait.rs | 89 ++++++++++++++++---- test/syscall_test/Makefile | 1 + test/syscall_test/blocklists/futex_test | 15 ++++ 8 files changed, 332 insertions(+), 74 deletions(-) create mode 100644 test/syscall_test/blocklists/futex_test diff --git a/kernel/src/process/posix_thread/futex.rs b/kernel/src/process/posix_thread/futex.rs index 53d76bf7..1decbde3 100644 --- a/kernel/src/process/posix_thread/futex.rs +++ b/kernel/src/process/posix_thread/futex.rs @@ -9,7 +9,7 @@ use ostd::{ }; use spin::Once; -use crate::prelude::*; +use crate::{prelude::*, time::wait::TimerBuilder}; type FutexBitSet = u32; type FutexBucketRef = Arc>; @@ -19,21 +19,38 @@ const FUTEX_FLAGS_MASK: u32 = 0xFFFF_FFF0; const FUTEX_BITSET_MATCH_ANY: FutexBitSet = 0xFFFF_FFFF; /// do futex wait -pub fn futex_wait(futex_addr: u64, futex_val: i32, timeout: &Option) -> Result<()> { - futex_wait_bitset(futex_addr as _, futex_val, timeout, FUTEX_BITSET_MATCH_ANY) +pub fn futex_wait( + futex_addr: u64, + futex_val: i32, + timer_builder: Option, + ctx: &Context, +) -> Result<()> { + futex_wait_bitset( + futex_addr as _, + futex_val, + timer_builder, + FUTEX_BITSET_MATCH_ANY, + ctx, + ) } -/// do futex wait bitset +/// Does futex wait bitset pub fn futex_wait_bitset( futex_addr: Vaddr, futex_val: i32, - timeout: &Option, + timer_builder: Option, bitset: FutexBitSet, + ctx: &Context, ) -> Result<()> { debug!( - "futex_wait_bitset addr: {:#x}, val: {}, timeout: {:?}, bitset: {:#x}", - futex_addr, futex_val, timeout, bitset + "futex_wait_bitset addr: {:#x}, val: {}, bitset: {:#x}", + futex_addr, futex_val, bitset ); + + if bitset == 0 { + return_errno_with_message!(Errno::EINVAL, "at least one bit should be set"); + } + let futex_key = FutexKey::new(futex_addr, bitset); let (futex_item, waiter) = FutexItem::create(futex_key); @@ -41,7 +58,7 @@ pub fn futex_wait_bitset( // lock futex bucket ref here to avoid data race let mut futex_bucket = futex_bucket_ref.lock(); - if !futex_key.load_val().is_ok_and(|val| val == futex_val) { + if !futex_key.load_val(ctx).is_ok_and(|val| val == futex_val) { return_errno_with_message!( Errno::EAGAIN, "futex value does not match or load_val failed" @@ -53,18 +70,15 @@ pub fn futex_wait_bitset( // drop lock drop(futex_bucket); - // TODO: wait on the futex item with a timeout. - waiter.wait(); - - Ok(()) + waiter.pause_timer_timeout(timer_builder.as_ref()) } -/// do futex wake +/// Does futex wake pub fn futex_wake(futex_addr: Vaddr, max_count: usize) -> Result { futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY) } -/// Do futex wake with bitset +/// Does futex wake with bitset pub fn futex_wake_bitset( futex_addr: Vaddr, max_count: usize, @@ -75,6 +89,10 @@ pub fn futex_wake_bitset( futex_addr, max_count, bitset ); + if bitset == 0 { + return_errno_with_message!(Errno::EINVAL, "at least one bit should be set"); + } + let futex_key = FutexKey::new(futex_addr, bitset); let (_, futex_bucket_ref) = get_futex_bucket(futex_key); let mut futex_bucket = futex_bucket_ref.lock(); @@ -84,7 +102,7 @@ pub fn futex_wake_bitset( Ok(res) } -/// Do futex requeue +/// Does futex requeue pub fn futex_requeue( futex_addr: Vaddr, max_nwakes: usize, @@ -153,15 +171,6 @@ pub fn init() { FUTEX_BUCKETS.call_once(|| FutexBucketVec::new(get_bucket_count())); } -#[derive(Debug, Clone)] -pub struct FutexTimeout {} - -impl FutexTimeout { - pub fn new() -> Self { - todo!() - } -} - struct FutexBucketVec { vec: Vec, } @@ -330,10 +339,10 @@ impl FutexKey { Self { addr, bitset } } - pub fn load_val(&self) -> Result { + pub fn load_val(&self, ctx: &Context) -> Result { // FIXME: how to implement a atomic load? warn!("implement an atomic load"); - CurrentUserSpace::get().read_val(self.addr) + ctx.get_user_space().read_val(self.addr) } pub fn addr(&self) -> Vaddr { diff --git a/kernel/src/process/signal/pause.rs b/kernel/src/process/signal/pause.rs index f9e686ff..5c934cd7 100644 --- a/kernel/src/process/signal/pause.rs +++ b/kernel/src/process/signal/pause.rs @@ -6,7 +6,10 @@ use ostd::sync::{WaitQueue, Waiter}; use super::sig_mask::SigMask; use crate::{ - prelude::*, process::posix_thread::PosixThreadExt, thread::Thread, time::wait::WaitTimeout, + prelude::*, + process::posix_thread::PosixThreadExt, + thread::Thread, + time::wait::{TimerBuilder, WaitTimeout}, }; /// `Pause` is an extension trait to make [`Waiter`] and [`WaitQueue`] signal aware. @@ -33,7 +36,7 @@ pub trait Pause: WaitTimeout { where F: FnMut() -> Option, { - self.pause_until_or_timeout_opt(cond, None) + self.pause_until_or_timer_timeout_opt(cond, None) } /// Pauses the execution of the current thread until the `cond` is met ( i.e., `cond()` @@ -56,7 +59,18 @@ pub trait Pause: WaitTimeout { return cond() .ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached")); } - self.pause_until_or_timeout_opt(cond, Some(timeout)) + let timer_builder = TimerBuilder::new(timeout); + self.pause_until_or_timer_timeout_opt(cond, Some(&timer_builder)) + } + + /// Similar to [`Pause::pause_until_or_timeout`]. + /// + /// The only difference is that the timeout is against the user-specified clock. + fn pause_until_or_timer_timeout(&self, cond: F, timer_builder: &TimerBuilder) -> Result + where + F: FnMut() -> Option, + { + self.pause_until_or_timer_timeout_opt(cond, Some(timer_builder)) } /// Pauses the execution of the current thread until the `cond` is met ( i.e., `cond()` @@ -71,14 +85,31 @@ pub trait Pause: WaitTimeout { /// /// [`ETIME`]: crate::error::Errno::ETIME /// [`EINTR`]: crate::error::Errno::EINTR - #[doc(hidden)] - fn pause_until_or_timeout_opt(&self, cond: F, timeout: Option<&Duration>) -> Result + fn pause_until_or_timer_timeout_opt( + &self, + cond: F, + timer_builder: Option<&TimerBuilder>, + ) -> Result where F: FnMut() -> Option; + + /// Pauses the execution of the current thread until being woken up, + /// or some signals are received by the current thread or process. + /// If the input `timeout` is set, the pausing will finish when the `timeout` is expired. + /// + /// # Errors + /// + /// If `timeout` is expired before woken up or some signals are received, + /// this method will return [`Errno::ETIME`]. + fn pause_timer_timeout(&self, timer_builder: Option<&TimerBuilder>) -> Result<()>; } impl Pause for Waiter { - fn pause_until_or_timeout_opt(&self, mut cond: F, timeout: Option<&Duration>) -> Result + fn pause_until_or_timer_timeout_opt( + &self, + mut cond: F, + timer_builder: Option<&TimerBuilder>, + ) -> Result where F: FnMut() -> Option, { @@ -88,9 +119,12 @@ impl Pause for Waiter { let current_thread = self.task().data().downcast_ref::>(); - let Some(posix_thread) = current_thread.and_then(|thread| thread.as_posix_thread()) else { - if let Some(timeout) = timeout { - return self.wait_until_or_timeout(cond, timeout); + let Some(posix_thread) = current_thread + .as_ref() + .and_then(|thread| thread.as_posix_thread()) + else { + if let Some(timer_builder) = timer_builder { + return self.wait_until_or_timer_timeout(cond, timer_builder); } else { return self.wait_until_or_cancelled(cond, || Ok(())); } @@ -107,18 +141,54 @@ impl Pause for Waiter { }; posix_thread.set_signalled_waker(self.waker()); - let res = if let Some(timeout) = timeout { - self.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout) + let res = if let Some(timer_builder) = timer_builder { + self.wait_until_or_timer_timeout_cancelled(cond, cancel_cond, timer_builder) } else { self.wait_until_or_cancelled(cond, cancel_cond) }; posix_thread.clear_signalled_waker(); res } + + fn pause_timer_timeout(&self, timer_builder: Option<&TimerBuilder>) -> Result<()> { + let timer = timer_builder.map(|timer_builder| { + let waker = self.waker(); + timer_builder.fire(move || { + waker.wake_up(); + }) + }); + + let current_thread = self.task().data().downcast_ref::>(); + + if let Some(posix_thread) = current_thread + .as_ref() + .and_then(|thread| thread.as_posix_thread()) + { + posix_thread.set_signalled_waker(self.waker()); + self.wait(); + posix_thread.clear_signalled_waker(); + } else { + self.wait(); + } + + if let Some(timer) = timer { + if timer.remain().is_zero() { + return_errno_with_message!(Errno::ETIME, "the timeout is reached"); + } + // If the timeout is not expired, cancel the timer manually. + timer.cancel(); + } + + Ok(()) + } } impl Pause for WaitQueue { - fn pause_until_or_timeout_opt(&self, mut cond: F, timeout: Option<&Duration>) -> Result + fn pause_until_or_timer_timeout_opt( + &self, + mut cond: F, + timer_builder: Option<&TimerBuilder>, + ) -> Result where F: FnMut() -> Option, { @@ -131,7 +201,11 @@ impl Pause for WaitQueue { self.enqueue(waiter.waker()); cond() }; - waiter.pause_until_or_timeout_opt(cond, timeout) + waiter.pause_until_or_timer_timeout_opt(cond, timer_builder) + } + + fn pause_timer_timeout(&self, _timer_builder: Option<&TimerBuilder>) -> Result<()> { + panic!("`pause_timer_timeout` can only be used on `Waiter`"); } } diff --git a/kernel/src/syscall/futex.rs b/kernel/src/syscall/futex.rs index 5e28a987..ec1712f4 100644 --- a/kernel/src/syscall/futex.rs +++ b/kernel/src/syscall/futex.rs @@ -1,19 +1,27 @@ // SPDX-License-Identifier: MPL-2.0 +use core::time::Duration; + use crate::{ prelude::*, process::posix_thread::futex::{ futex_op_and_flags_from_u32, futex_requeue, futex_wait, futex_wait_bitset, futex_wake, - futex_wake_bitset, FutexOp, FutexTimeout, + futex_wake_bitset, FutexFlags, FutexOp, }, syscall::SyscallReturn, + time::{ + clocks::{MonotonicClock, RealTimeClock}, + timer::Timeout, + timespec_t, + wait::TimerBuilder, + }, }; pub fn sys_futex( futex_addr: Vaddr, futex_op: i32, - futex_val: u32, - utime_addr: u64, + futex_val: u64, + utime_addr: Vaddr, futex_new_addr: u64, bitset: u64, ctx: &Context, @@ -21,8 +29,8 @@ pub fn sys_futex( // FIXME: we current ignore futex flags let (futex_op, futex_flags) = futex_op_and_flags_from_u32(futex_op as _)?; debug!( - "futex_op = {:?}, futex_flags = {:?}, futex_addr = 0x{:x}", - futex_op, futex_flags, futex_addr + "futex_op = {:?}, futex_flags = {:?}, futex_addr = 0x{:x}, futex_val = 0x{:x}", + futex_op, futex_flags, futex_addr, futex_val ); let get_futex_val = |val: i32| -> Result { @@ -32,22 +40,65 @@ pub fn sys_futex( Ok(val as usize) }; - let get_futex_timeout = |timeout_addr| -> Result> { + let get_futex_timer_builder = |timeout_addr: Vaddr| -> Result>> { if timeout_addr == 0 { return Ok(None); } - // TODO: parse a timeout - todo!() + + let timeout = { + let time_spec: timespec_t = CurrentUserSpace::get().read_val(timeout_addr)?; + Duration::try_from(time_spec)? + }; + + let is_real_time = futex_flags.contains(FutexFlags::FUTEX_CLOCK_REALTIME); + if is_real_time && futex_op == FutexOp::FUTEX_WAIT { + // Ref: + return_errno_with_message!(Errno::ENOSYS, "FUTEX_WAIT cannot use CLOCK_REALTIME"); + } + + let timeout = { + // From man(2) futex: + // for FUTEX_WAIT, timeout is interpreted as a relative value. + // This differs from other futex operations, + // where timeout is interpreted as an absolute value. + // To obtain the equivalent of FUTEX_WAIT with an absolute timeout, + // employ FUTEX_WAIT_BITSET with val3 specified as FUTEX_BITSET_MATCH_ANY. + if futex_op == FutexOp::FUTEX_WAIT { + Timeout::After(timeout) + } else { + Timeout::When(timeout) + } + }; + + let timer_manager = if is_real_time { + debug!("futex timeout = {:?}, clock = CLOCK_REALTIME", timeout); + RealTimeClock::timer_manager() + } else { + debug!("futex timeout = {:?}, clock = CLOCK_MONOTONIC", timeout); + MonotonicClock::timer_manager() + }; + + Ok(Some(TimerBuilder::new_with_timer_manager( + timeout, + timer_manager, + ))) }; let res = match futex_op { FutexOp::FUTEX_WAIT => { - let timeout = get_futex_timeout(utime_addr)?; - futex_wait(futex_addr as _, futex_val as _, &timeout).map(|_| 0) + let timer_builder = get_futex_timer_builder(utime_addr)?; + futex_wait(futex_addr as _, futex_val as _, timer_builder, ctx).map(|_| 0) } FutexOp::FUTEX_WAIT_BITSET => { - let timeout = get_futex_timeout(utime_addr)?; - futex_wait_bitset(futex_addr as _, futex_val as _, &timeout, bitset as _).map(|_| 0) + let timer_builder = get_futex_timer_builder(utime_addr)?; + futex_wait_bitset( + futex_addr as _, + futex_val as _, + timer_builder, + bitset as _, + ctx, + ) + .map(|_| 0) } FutexOp::FUTEX_WAKE => { let max_count = get_futex_val(futex_val as i32)?; @@ -68,8 +119,19 @@ pub fn sys_futex( ) .map(|nwakes| nwakes as _) } - _ => panic!("Unsupported futex operations"), - }?; + _ => { + warn!("futex op = {:?}", futex_op); + return_errno_with_message!(Errno::EINVAL, "unsupported futex op"); + } + } + .map_err(|e| { + // From Linux manual, Futex returns `ETIMEDOUT` instead of `ETIME` + if e.error() == Errno::ETIME { + Error::with_message(Errno::ETIMEDOUT, "futex wait timeout") + } else { + e + } + })?; debug!("futex returns, tid= {} ", ctx.posix_thread.tid()); Ok(SyscallReturn::Return(res as _)) diff --git a/kernel/src/syscall/nanosleep.rs b/kernel/src/syscall/nanosleep.rs index bd579df0..394cdd4b 100644 --- a/kernel/src/syscall/nanosleep.rs +++ b/kernel/src/syscall/nanosleep.rs @@ -7,7 +7,14 @@ use ostd::sync::Waiter; use super::{clock_gettime::read_clock, ClockId, SyscallReturn}; use crate::{ prelude::*, - time::{clockid_t, timespec_t, TIMER_ABSTIME}, + time::{ + clockid_t, + clocks::{BootTimeClock, MonotonicClock, RealTimeClock}, + timer::Timeout, + timespec_t, + wait::TimerBuilder, + TIMER_ABSTIME, + }, }; pub fn sys_nanosleep( @@ -70,7 +77,7 @@ fn do_clock_nanosleep( ); let start_time = read_clock(clockid, ctx)?; - let timeout = if is_abs_time { + let duration = if is_abs_time { if request_time < start_time { return Ok(SyscallReturn::Return(0)); } @@ -84,18 +91,39 @@ fn do_clock_nanosleep( // current process. i.e., the signals that should be ignored will not interrupt sleeping thread. let waiter = Waiter::new_pair().0; - let res = waiter.pause_until_or_timeout(|| None, &timeout); + let timer_manager = { + let clock_id = ClockId::try_from(clockid)?; + match clock_id { + ClockId::CLOCK_BOOTTIME => BootTimeClock::timer_manager(), + ClockId::CLOCK_MONOTONIC => MonotonicClock::timer_manager(), + ClockId::CLOCK_REALTIME => RealTimeClock::timer_manager(), + // FIXME: We should better not expose this prof timer manager. + ClockId::CLOCK_PROCESS_CPUTIME_ID => { + ctx.process.timer_manager().prof_timer().timer_manager() + } + // FIXME: From the manual, + // the CPU clock IDs returned by clock_getcpuclockid(3) + // and pthread_getcpuclockid(3) can also be passed in clockid. + // But it's not covered here. + _ => return_errno_with_message!(Errno::EINVAL, "unknown clockid for clock_nanosleep"), + } + }; + + let timer_builder = + TimerBuilder::new_with_timer_manager(Timeout::After(duration), timer_manager); + + let res = waiter.pause_until_or_timer_timeout(|| None, &timer_builder); match res { Err(e) if e.error() == Errno::ETIME => Ok(SyscallReturn::Return(0)), Err(e) if e.error() == Errno::EINTR => { let end_time = read_clock(clockid, ctx)?; - if end_time >= start_time + timeout { + if end_time >= start_time + duration { return Ok(SyscallReturn::Return(0)); } if remain_timespec_addr != 0 && !is_abs_time { - let remaining_duration = (start_time + timeout) - end_time; + let remaining_duration = (start_time + duration) - end_time; let remaining_timespec = timespec_t::from(remaining_duration); ctx.get_user_space() .write_val(remain_timespec_addr, &remaining_timespec)?; diff --git a/kernel/src/time/core/timer.rs b/kernel/src/time/core/timer.rs index 0d8530f2..36d51e57 100644 --- a/kernel/src/time/core/timer.rs +++ b/kernel/src/time/core/timer.rs @@ -16,6 +16,7 @@ use ostd::sync::SpinLock; use super::Clock; /// A timeout, represented in one of the two ways. +#[derive(Debug, Clone)] pub enum Timeout { /// The timeout is reached _after_ the `Duration` time is elapsed. After(Duration), @@ -159,6 +160,17 @@ impl TimerManager { }) } + /// Returns whether a given `timeout` is expired. + pub fn is_expired_timeout(&self, timeout: &Timeout) -> bool { + match timeout { + Timeout::After(duration) => *duration == Duration::ZERO, + Timeout::When(duration) => { + let now = self.clock.read_time(); + now >= *duration + } + } + } + fn insert(&self, timer_callback: Arc) { self.timer_callbacks .disable_irq() diff --git a/kernel/src/time/wait.rs b/kernel/src/time/wait.rs index 691633dd..39a89831 100644 --- a/kernel/src/time/wait.rs +++ b/kernel/src/time/wait.rs @@ -4,7 +4,7 @@ use core::time::Duration; use ostd::sync::{WaitQueue, Waiter}; -use super::{clocks::JIFFIES_TIMER_MANAGER, timer::Timeout}; +use super::{clocks::JIFFIES_TIMER_MANAGER, timer::Timeout, Timer, TimerManager}; use crate::prelude::*; /// A trait that provide the timeout related function for [`Waiter`] and [`WaitQueue`]`. @@ -16,7 +16,19 @@ pub trait WaitTimeout { where F: FnMut() -> Option, { - self.wait_until_or_timeout_cancelled(cond, || Ok(()), timeout) + let timer_builder = TimerBuilder::new(timeout); + self.wait_until_or_timer_timeout(cond, &timer_builder) + } + + /// Similar to [`WaitTimeout::wait_until_or_timeout`]. + /// + /// The difference is that the timeout of this method is against the specified clock, + /// which is defined in `timer_builder`. + fn wait_until_or_timer_timeout(&self, cond: F, timer_builder: &TimerBuilder) -> Result + where + F: FnMut() -> Option, + { + self.wait_until_or_timer_timeout_cancelled(cond, || Ok(()), timer_builder) } /// Waits until some condition returns `Some(_)`, or be cancelled due to @@ -24,29 +36,75 @@ pub trait WaitTimeout { /// does not becomes `Some(_)` before the timeout is reached or `cancel_cond` /// returns `Err`, this function will return corresponding `Err`. #[doc(hidden)] - fn wait_until_or_timeout_cancelled( + fn wait_until_or_timer_timeout_cancelled( &self, cond: F, cancel_cond: FCancel, - timeout: &Duration, + timer_builder: &TimerBuilder, ) -> Result where F: FnMut() -> Option, FCancel: Fn() -> Result<()>; } +/// A helper structure to build timers from a specified timeout and timer manager. +pub struct TimerBuilder<'a> { + timeout: Timeout, + timer_manager: &'a Arc, +} + +impl<'a> TimerBuilder<'a> { + /// Creates a new `TimerBuilder` against the default JIFFIES clock. + pub fn new(timeout: &Duration) -> Self { + let timeout = Timeout::After(*timeout); + let jiffies_timer_manager = JIFFIES_TIMER_MANAGER.get().unwrap(); + Self::new_with_timer_manager(timeout, jiffies_timer_manager) + } + + /// Creates a new `TimerBuilder` with given timer manager. + pub const fn new_with_timer_manager( + timeout: Timeout, + timer_manager: &'a Arc, + ) -> Self { + Self { + timeout, + timer_manager, + } + } + + /// Returns the timeout + pub const fn timeout(&self) -> &Timeout { + &self.timeout + } + + fn is_expired(&self) -> bool { + self.timer_manager.is_expired_timeout(&self.timeout) + } + + /// Builds and sets a timer, + /// which will trigger `callback` when `self.timeout()` is reached. + pub fn fire(&self, callback: F) -> Arc + where + F: Fn() + Send + Sync + 'static, + { + let timer = self.timer_manager.create_timer(callback); + timer.set_timeout(self.timeout.clone()); + timer + } +} + impl WaitTimeout for Waiter { - fn wait_until_or_timeout_cancelled( + fn wait_until_or_timer_timeout_cancelled( &self, mut cond: F, cancel_cond: FCancel, - timeout: &Duration, + timer_builder: &TimerBuilder, ) -> Result where F: FnMut() -> Option, FCancel: Fn() -> Result<()>, { - if *timeout == Duration::ZERO { + if timer_builder.is_expired() { return cond() .ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached")); } @@ -56,15 +114,14 @@ impl WaitTimeout for Waiter { } let waker = self.waker(); - let jiffies_timer = JIFFIES_TIMER_MANAGER.get().unwrap().create_timer(move || { + let timer = timer_builder.fire(move || { waker.wake_up(); }); - jiffies_timer.set_timeout(Timeout::After(*timeout)); let timeout_cond = { - let jiffies_timer = jiffies_timer.clone(); + let timer = timer.clone(); move || { - if jiffies_timer.remain() != Duration::ZERO { + if timer.remain() != Duration::ZERO { Ok(()) } else { Err(Error::with_message( @@ -88,7 +145,7 @@ impl WaitTimeout for Waiter { .as_ref() .is_err_and(|e: &Error| e.error() == Errno::ETIME) { - jiffies_timer.cancel(); + timer.cancel(); } res @@ -96,17 +153,17 @@ impl WaitTimeout for Waiter { } impl WaitTimeout for WaitQueue { - fn wait_until_or_timeout_cancelled( + fn wait_until_or_timer_timeout_cancelled( &self, mut cond: F, cancel_cond: FCancel, - timeout: &Duration, + timer_builder: &TimerBuilder, ) -> Result where F: FnMut() -> Option, FCancel: Fn() -> Result<()>, { - if *timeout == Duration::ZERO { + if timer_builder.is_expired() { return cond() .ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached")); } @@ -120,6 +177,6 @@ impl WaitTimeout for WaitQueue { self.enqueue(waiter.waker()); cond() }; - waiter.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout) + waiter.wait_until_or_timer_timeout_cancelled(cond, cancel_cond, timer_builder) } } diff --git a/test/syscall_test/Makefile b/test/syscall_test/Makefile index 6c662d4a..9d6be9dc 100644 --- a/test/syscall_test/Makefile +++ b/test/syscall_test/Makefile @@ -18,6 +18,7 @@ TESTS ?= \ fcntl_test \ flock_test \ fsync_test \ + futex_test \ getdents_test \ link_test \ lseek_test \ diff --git a/test/syscall_test/blocklists/futex_test b/test/syscall_test/blocklists/futex_test new file mode 100644 index 00000000..e6e44279 --- /dev/null +++ b/test/syscall_test/blocklists/futex_test @@ -0,0 +1,15 @@ +PrivateFutexTest.* +RobustFutexTest.* +SharedFutexTest.WakeInterprocessFile_NoRandomSave +SharedPrivate/PrivateAndSharedFutexTest.Wake0_NoRandomSave/* +SharedPrivate/PrivateAndSharedFutexTest.WakeOpCondSuccess_NoRandomSave/* +SharedPrivate/PrivateAndSharedFutexTest.WakeOpCondFailure_NoRandomSave/* +SharedPrivate/PrivateAndSharedFutexTest.PIBasic/* +SharedPrivate/PrivateAndSharedFutexTest.PIConcurrency_NoRandomSave/* +SharedPrivate/PrivateAndSharedFutexTest.PIWaiters/* +SharedPrivate/PrivateAndSharedFutexTest.PITryLock/* +SharedPrivate/PrivateAndSharedFutexTest.PITryLockConcurrency_NoRandomSave/* + +SharedPrivate/PrivateAndSharedFutexTest.WakeSome_NoRandomSave/1 +SharedPrivate/PrivateAndSharedFutexTest.NoWakeInterprocessPrivateAnon_NoRandomSave/* +SharedPrivate/PrivateAndSharedFutexTest.WakeWrongKind_NoRandomSave/*