diff --git a/kernel/src/process/posix_thread/futex.rs b/kernel/src/process/posix_thread/futex.rs index c68d9752..316c0887 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::*, process::Pid, time::wait::TimerBuilder}; +use crate::{prelude::*, process::Pid, time::wait::ManagedTimeout}; type FutexBitSet = u32; type FutexBucketRef = Arc>; @@ -22,14 +22,14 @@ const FUTEX_BITSET_MATCH_ANY: FutexBitSet = 0xFFFF_FFFF; pub fn futex_wait( futex_addr: u64, futex_val: i32, - timer_builder: Option, + timeout: Option, ctx: &Context, pid: Option, ) -> Result<()> { futex_wait_bitset( futex_addr as _, futex_val, - timer_builder, + timeout, FUTEX_BITSET_MATCH_ANY, ctx, pid, @@ -40,7 +40,7 @@ pub fn futex_wait( pub fn futex_wait_bitset( futex_addr: Vaddr, futex_val: i32, - timer_builder: Option, + timeout: Option, bitset: FutexBitSet, ctx: &Context, pid: Option, @@ -73,7 +73,7 @@ pub fn futex_wait_bitset( // drop lock drop(futex_bucket); - waiter.pause_timer_timeout(timer_builder.as_ref()) + waiter.pause_timeout(timeout) } /// Does futex wake diff --git a/kernel/src/process/signal/pause.rs b/kernel/src/process/signal/pause.rs index 5c934cd7..f6080393 100644 --- a/kernel/src/process/signal/pause.rs +++ b/kernel/src/process/signal/pause.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 -use core::{sync::atomic::Ordering, time::Duration}; +use core::sync::atomic::Ordering; use ostd::sync::{WaitQueue, Waiter}; @@ -9,7 +9,7 @@ use crate::{ prelude::*, process::posix_thread::PosixThreadExt, thread::Thread, - time::wait::{TimerBuilder, WaitTimeout}, + time::wait::{ManagedTimeout, TimeoutExt}, }; /// `Pause` is an extension trait to make [`Waiter`] and [`WaitQueue`] signal aware. @@ -36,7 +36,7 @@ pub trait Pause: WaitTimeout { where F: FnMut() -> Option, { - self.pause_until_or_timer_timeout_opt(cond, None) + self.pause_until_or_timeout_impl(cond, None) } /// Pauses the execution of the current thread until the `cond` is met ( i.e., `cond()` @@ -51,26 +51,18 @@ pub trait Pause: WaitTimeout { /// /// [`ETIME`]: crate::error::Errno::ETIME /// [`EINTR`]: crate::error::Errno::EINTR - fn pause_until_or_timeout(&self, mut cond: F, timeout: &Duration) -> Result + fn pause_until_or_timeout<'a, F, T, R>(&self, mut cond: F, timeout: T) -> Result where F: FnMut() -> Option, + T: Into>, { - if *timeout == Duration::ZERO { - return cond() - .ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached")); - } - let timer_builder = TimerBuilder::new(timeout); - self.pause_until_or_timer_timeout_opt(cond, Some(&timer_builder)) - } + let timeout = timeout.into(); + let timeout_inner = match timeout.check_expired() { + Ok(inner) => inner, + Err(err) => return cond().ok_or(err), + }; - /// 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)) + self.pause_until_or_timeout_impl(cond, timeout_inner) } /// Pauses the execution of the current thread until the `cond` is met ( i.e., `cond()` @@ -85,10 +77,11 @@ pub trait Pause: WaitTimeout { /// /// [`ETIME`]: crate::error::Errno::ETIME /// [`EINTR`]: crate::error::Errno::EINTR - fn pause_until_or_timer_timeout_opt( + #[doc(hidden)] + fn pause_until_or_timeout_impl( &self, cond: F, - timer_builder: Option<&TimerBuilder>, + timeout: Option<&ManagedTimeout>, ) -> Result where F: FnMut() -> Option; @@ -101,21 +94,20 @@ pub trait Pause: WaitTimeout { /// /// 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<()>; + fn pause_timeout<'a>(&self, timeout: impl Into>) -> Result<()>; } impl Pause for Waiter { - fn pause_until_or_timer_timeout_opt( + fn pause_until_or_timeout_impl( &self, - mut cond: F, - timer_builder: Option<&TimerBuilder>, + cond: F, + timeout: Option<&ManagedTimeout>, ) -> Result where F: FnMut() -> Option, { - if let Some(res) = cond() { - return Ok(res); - } + // No fast paths for `Waiter`. If the caller wants a fast path, it should do so _before_ + // the waiter is created. let current_thread = self.task().data().downcast_ref::>(); @@ -123,11 +115,7 @@ impl Pause for Waiter { .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(())); - } + return self.wait_until_or_timeout_cancelled(cond, || Ok(()), timeout); }; let cancel_cond = || { @@ -141,19 +129,16 @@ impl Pause for Waiter { }; posix_thread.set_signalled_waker(self.waker()); - 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) - }; + let res = self.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout); posix_thread.clear_signalled_waker(); + res } - fn pause_timer_timeout(&self, timer_builder: Option<&TimerBuilder>) -> Result<()> { - let timer = timer_builder.map(|timer_builder| { + fn pause_timeout<'a>(&self, timeout: impl Into>) -> Result<()> { + let timer = timeout.into().check_expired()?.map(|timeout| { let waker = self.waker(); - timer_builder.fire(move || { + timeout.create_timer(move || { waker.wake_up(); }) }); @@ -184,14 +169,15 @@ impl Pause for Waiter { } impl Pause for WaitQueue { - fn pause_until_or_timer_timeout_opt( + fn pause_until_or_timeout_impl( &self, mut cond: F, - timer_builder: Option<&TimerBuilder>, + timeout: Option<&ManagedTimeout>, ) -> Result where F: FnMut() -> Option, { + // Fast path: if let Some(res) = cond() { return Ok(res); } @@ -201,11 +187,11 @@ impl Pause for WaitQueue { self.enqueue(waiter.waker()); cond() }; - waiter.pause_until_or_timer_timeout_opt(cond, timer_builder) + waiter.pause_until_or_timeout_impl(cond, timeout) } - fn pause_timer_timeout(&self, _timer_builder: Option<&TimerBuilder>) -> Result<()> { - panic!("`pause_timer_timeout` can only be used on `Waiter`"); + fn pause_timeout<'a>(&self, _timeout: impl Into>) -> Result<()> { + panic!("`pause_timeout` can only be used on `Waiter`"); } } diff --git a/kernel/src/process/signal/poll.rs b/kernel/src/process/signal/poll.rs index 9b07dd59..a070698f 100644 --- a/kernel/src/process/signal/poll.rs +++ b/kernel/src/process/signal/poll.rs @@ -209,11 +209,7 @@ impl EventCounter { } }; - if let Some(timeout) = timeout { - waiter.pause_until_or_timeout(cond, timeout) - } else { - waiter.pause_until(cond) - } + waiter.pause_until_or_timeout(cond, timeout) } pub fn write(&self) { diff --git a/kernel/src/syscall/futex.rs b/kernel/src/syscall/futex.rs index 0fc4ee05..904544f1 100644 --- a/kernel/src/syscall/futex.rs +++ b/kernel/src/syscall/futex.rs @@ -14,7 +14,7 @@ use crate::{ clocks::{MonotonicClock, RealTimeClock}, timer::Timeout, timespec_t, - wait::TimerBuilder, + wait::ManagedTimeout, }, }; @@ -40,7 +40,7 @@ pub fn sys_futex( Ok(val as usize) }; - let get_futex_timer_builder = |timeout_addr: Vaddr| -> Result>> { + let get_futex_timeout = |timeout_addr: Vaddr| -> Result>> { if timeout_addr == 0 { return Ok(None); } @@ -78,7 +78,7 @@ pub fn sys_futex( MonotonicClock::timer_manager() }; - Ok(Some(TimerBuilder::new_with_timer_manager( + Ok(Some(ManagedTimeout::new_with_manager( timeout, timer_manager, ))) @@ -91,15 +91,15 @@ pub fn sys_futex( }; let res = match futex_op { FutexOp::FUTEX_WAIT => { - let timer_builder = get_futex_timer_builder(utime_addr)?; - futex_wait(futex_addr as _, futex_val as _, timer_builder, ctx, pid).map(|_| 0) + let timeout = get_futex_timeout(utime_addr)?; + futex_wait(futex_addr as _, futex_val as _, timeout, ctx, pid).map(|_| 0) } FutexOp::FUTEX_WAIT_BITSET => { - let timer_builder = get_futex_timer_builder(utime_addr)?; + let timeout = get_futex_timeout(utime_addr)?; futex_wait_bitset( futex_addr as _, futex_val as _, - timer_builder, + timeout, bitset as _, ctx, pid, diff --git a/kernel/src/syscall/nanosleep.rs b/kernel/src/syscall/nanosleep.rs index 394cdd4b..5eaa1d1c 100644 --- a/kernel/src/syscall/nanosleep.rs +++ b/kernel/src/syscall/nanosleep.rs @@ -12,7 +12,7 @@ use crate::{ clocks::{BootTimeClock, MonotonicClock, RealTimeClock}, timer::Timeout, timespec_t, - wait::TimerBuilder, + wait::ManagedTimeout, TIMER_ABSTIME, }, }; @@ -109,10 +109,11 @@ fn do_clock_nanosleep( } }; - let timer_builder = - TimerBuilder::new_with_timer_manager(Timeout::After(duration), timer_manager); + let res = waiter.pause_until_or_timeout( + || None, + ManagedTimeout::new_with_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 => { diff --git a/kernel/src/time/wait.rs b/kernel/src/time/wait.rs index 39a89831..c8c92227 100644 --- a/kernel/src/time/wait.rs +++ b/kernel/src/time/wait.rs @@ -12,23 +12,18 @@ pub trait WaitTimeout { /// Waits until some condition returns `Some(_)`, or a given timeout is reached. If /// the condition does not becomes `Some(_)` before the timeout is reached, /// this function will return `ETIME` error. - fn wait_until_or_timeout(&self, cond: F, timeout: &Duration) -> Result + fn wait_until_or_timeout<'a, F, T, R>(&self, mut cond: F, timeout: T) -> Result where F: FnMut() -> Option, + T: Into>, { - let timer_builder = TimerBuilder::new(timeout); - self.wait_until_or_timer_timeout(cond, &timer_builder) - } + let timeout = timeout.into(); + let timeout_inner = match timeout.check_expired() { + Ok(inner) => inner, + Err(err) => return cond().ok_or(err), + }; - /// 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) + self.wait_until_or_timeout_cancelled(cond, || Ok(()), timeout_inner) } /// Waits until some condition returns `Some(_)`, or be cancelled due to @@ -36,114 +31,152 @@ 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_timer_timeout_cancelled( + fn wait_until_or_timeout_cancelled( &self, cond: F, cancel_cond: FCancel, - timer_builder: &TimerBuilder, + timeout: Option<&ManagedTimeout>, ) -> 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, +/// A timeout with extended semantics. +pub enum TimeoutExt<'a> { + /// The timeout will never fire. + Never, + /// The timeout will expire later according to [`ManagedTimeout`]. + At(ManagedTimeout<'a>), } -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, +impl<'a> TimeoutExt<'a> { + /// Checks whether the timeout is expired. + /// + /// This method will return: + /// - `Ok(Some(_))` if the timeout isn't expired but it may be expired later. + /// - `Ok(None)` if the timeout will never be expired. + /// - `Err(ETIME)` if the timeout is expired. + pub fn check_expired(&self) -> Result>> { + match self { + TimeoutExt::At(inner) if inner.is_expired() => { + return_errno_with_message!(Errno::ETIME, "the time limit is reached") + } + TimeoutExt::At(inner) => Ok(Some(inner)), + TimeoutExt::Never => Ok(None), } } +} - /// Returns the timeout - pub const fn timeout(&self) -> &Timeout { - &self.timeout +impl From<&Duration> for TimeoutExt<'_> { + fn from(value: &Duration) -> Self { + Self::At(ManagedTimeout::new(*value)) + } +} + +impl From> for TimeoutExt<'_> { + fn from(value: Option<&Duration>) -> Self { + match value { + Some(duration) => duration.into(), + None => Self::Never, + } + } +} + +impl<'a> From> for TimeoutExt<'a> { + fn from(value: ManagedTimeout<'a>) -> Self { + Self::At(value) + } +} + +impl<'a> From>> for TimeoutExt<'a> { + fn from(value: Option>) -> Self { + match value { + Some(timeout) => timeout.into(), + None => Self::Never, + } + } +} + +/// A [`Timeout`] with the associated [`TimerManager`]. +pub struct ManagedTimeout<'a> { + timeout: Timeout, + manager: &'a Arc, +} + +impl<'a> ManagedTimeout<'a> { + /// Creates a new `ManagedTimeout` with the JIFFIES timer manager. + pub fn new(timeout: Duration) -> Self { + let timeout = Timeout::After(timeout); + let manager = JIFFIES_TIMER_MANAGER.get().unwrap(); + Self::new_with_manager(timeout, manager) } - fn is_expired(&self) -> bool { - self.timer_manager.is_expired_timeout(&self.timeout) + /// Creates a new `ManagedTimeout` with the given timer manager. + pub const fn new_with_manager(timeout: Timeout, manager: &'a Arc) -> Self { + Self { timeout, manager } } - /// Builds and sets a timer, - /// which will trigger `callback` when `self.timeout()` is reached. - pub fn fire(&self, callback: F) -> Arc + /// Returns weather the timeout is expired. + pub fn is_expired(&self) -> bool { + self.manager.is_expired_timeout(&self.timeout) + } + + /// Creates a timer for the timeout. + pub fn create_timer(&self, callback: F) -> Arc where F: Fn() + Send + Sync + 'static, { - let timer = self.timer_manager.create_timer(callback); + let timer = self.manager.create_timer(callback); timer.set_timeout(self.timeout.clone()); timer } } impl WaitTimeout for Waiter { - fn wait_until_or_timer_timeout_cancelled( + fn wait_until_or_timeout_cancelled( &self, - mut cond: F, + cond: F, cancel_cond: FCancel, - timer_builder: &TimerBuilder, + timeout: Option<&ManagedTimeout>, ) -> Result where F: FnMut() -> Option, FCancel: Fn() -> Result<()>, { - if timer_builder.is_expired() { - return cond() - .ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached")); - } + // No fast paths for `Waiter`. If the caller wants a fast path, it should do so _before_ + // the waiter is created. - if let Some(res) = cond() { - return Ok(res); - } - - let waker = self.waker(); - let timer = timer_builder.fire(move || { - waker.wake_up(); + let timer = timeout.map(|timeout| { + let waker = self.waker(); + timeout.create_timer(move || { + waker.wake_up(); + }) }); - let timeout_cond = { + let cancel_cond = { let timer = timer.clone(); - move || { - if timer.remain() != Duration::ZERO { - Ok(()) - } else { - Err(Error::with_message( - Errno::ETIME, - "the time limit is reached", - )) - } - } - }; - let cancel_cond = || { - timeout_cond()?; - cancel_cond() + move || { + if timer + .as_ref() + .is_some_and(|timer| timer.remain() == Duration::ZERO) + { + return_errno_with_message!(Errno::ETIME, "the time limit is reached"); + } + + cancel_cond() + } }; let res = self.wait_until_or_cancelled(cond, cancel_cond); // If `res` is not `ETIME` error, then the timeout may not have been expired. // We cancel it manually. - if !res - .as_ref() - .is_err_and(|e: &Error| e.error() == Errno::ETIME) + if let Some(timer) = timer + && !res + .as_ref() + .is_err_and(|e: &Error| e.error() == Errno::ETIME) { timer.cancel(); } @@ -153,21 +186,17 @@ impl WaitTimeout for Waiter { } impl WaitTimeout for WaitQueue { - fn wait_until_or_timer_timeout_cancelled( + fn wait_until_or_timeout_cancelled( &self, mut cond: F, cancel_cond: FCancel, - timer_builder: &TimerBuilder, + timeout: Option<&ManagedTimeout>, ) -> Result where F: FnMut() -> Option, FCancel: Fn() -> Result<()>, { - if timer_builder.is_expired() { - return cond() - .ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached")); - } - + // Fast path: if let Some(res) = cond() { return Ok(res); } @@ -177,6 +206,6 @@ impl WaitTimeout for WaitQueue { self.enqueue(waiter.waker()); cond() }; - waiter.wait_until_or_timer_timeout_cancelled(cond, cancel_cond, timer_builder) + waiter.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout) } }