Make *_or_timeout APIs concise

This commit is contained in:
Ruihan Li
2024-11-02 13:28:43 +08:00
committed by Tate, Hongliang Tian
parent 10cc85d588
commit 32b8e05cc3
6 changed files with 163 additions and 151 deletions

View File

@ -9,7 +9,7 @@ use ostd::{
}; };
use spin::Once; use spin::Once;
use crate::{prelude::*, process::Pid, time::wait::TimerBuilder}; use crate::{prelude::*, process::Pid, time::wait::ManagedTimeout};
type FutexBitSet = u32; type FutexBitSet = u32;
type FutexBucketRef = Arc<Mutex<FutexBucket>>; type FutexBucketRef = Arc<Mutex<FutexBucket>>;
@ -22,14 +22,14 @@ const FUTEX_BITSET_MATCH_ANY: FutexBitSet = 0xFFFF_FFFF;
pub fn futex_wait( pub fn futex_wait(
futex_addr: u64, futex_addr: u64,
futex_val: i32, futex_val: i32,
timer_builder: Option<TimerBuilder>, timeout: Option<ManagedTimeout>,
ctx: &Context, ctx: &Context,
pid: Option<Pid>, pid: Option<Pid>,
) -> Result<()> { ) -> Result<()> {
futex_wait_bitset( futex_wait_bitset(
futex_addr as _, futex_addr as _,
futex_val, futex_val,
timer_builder, timeout,
FUTEX_BITSET_MATCH_ANY, FUTEX_BITSET_MATCH_ANY,
ctx, ctx,
pid, pid,
@ -40,7 +40,7 @@ pub fn futex_wait(
pub fn futex_wait_bitset( pub fn futex_wait_bitset(
futex_addr: Vaddr, futex_addr: Vaddr,
futex_val: i32, futex_val: i32,
timer_builder: Option<TimerBuilder>, timeout: Option<ManagedTimeout>,
bitset: FutexBitSet, bitset: FutexBitSet,
ctx: &Context, ctx: &Context,
pid: Option<Pid>, pid: Option<Pid>,
@ -73,7 +73,7 @@ pub fn futex_wait_bitset(
// drop lock // drop lock
drop(futex_bucket); drop(futex_bucket);
waiter.pause_timer_timeout(timer_builder.as_ref()) waiter.pause_timeout(timeout)
} }
/// Does futex wake /// Does futex wake

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use core::{sync::atomic::Ordering, time::Duration}; use core::sync::atomic::Ordering;
use ostd::sync::{WaitQueue, Waiter}; use ostd::sync::{WaitQueue, Waiter};
@ -9,7 +9,7 @@ use crate::{
prelude::*, prelude::*,
process::posix_thread::PosixThreadExt, process::posix_thread::PosixThreadExt,
thread::Thread, thread::Thread,
time::wait::{TimerBuilder, WaitTimeout}, time::wait::{ManagedTimeout, TimeoutExt},
}; };
/// `Pause` is an extension trait to make [`Waiter`] and [`WaitQueue`] signal aware. /// `Pause` is an extension trait to make [`Waiter`] and [`WaitQueue`] signal aware.
@ -36,7 +36,7 @@ pub trait Pause: WaitTimeout {
where where
F: FnMut() -> Option<R>, F: FnMut() -> Option<R>,
{ {
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()` /// 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 /// [`ETIME`]: crate::error::Errno::ETIME
/// [`EINTR`]: crate::error::Errno::EINTR /// [`EINTR`]: crate::error::Errno::EINTR
fn pause_until_or_timeout<F, R>(&self, mut cond: F, timeout: &Duration) -> Result<R> fn pause_until_or_timeout<'a, F, T, R>(&self, mut cond: F, timeout: T) -> Result<R>
where where
F: FnMut() -> Option<R>, F: FnMut() -> Option<R>,
T: Into<TimeoutExt<'a>>,
{ {
if *timeout == Duration::ZERO { let timeout = timeout.into();
return cond() let timeout_inner = match timeout.check_expired() {
.ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached")); Ok(inner) => inner,
} Err(err) => return cond().ok_or(err),
let timer_builder = TimerBuilder::new(timeout); };
self.pause_until_or_timer_timeout_opt(cond, Some(&timer_builder))
}
/// Similar to [`Pause::pause_until_or_timeout`]. self.pause_until_or_timeout_impl(cond, timeout_inner)
///
/// The only difference is that the timeout is against the user-specified clock.
fn pause_until_or_timer_timeout<F, R>(&self, cond: F, timer_builder: &TimerBuilder) -> Result<R>
where
F: FnMut() -> Option<R>,
{
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()` /// 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 /// [`ETIME`]: crate::error::Errno::ETIME
/// [`EINTR`]: crate::error::Errno::EINTR /// [`EINTR`]: crate::error::Errno::EINTR
fn pause_until_or_timer_timeout_opt<F, R>( #[doc(hidden)]
fn pause_until_or_timeout_impl<F, R>(
&self, &self,
cond: F, cond: F,
timer_builder: Option<&TimerBuilder>, timeout: Option<&ManagedTimeout>,
) -> Result<R> ) -> Result<R>
where where
F: FnMut() -> Option<R>; F: FnMut() -> Option<R>;
@ -101,21 +94,20 @@ pub trait Pause: WaitTimeout {
/// ///
/// If `timeout` is expired before woken up or some signals are received, /// If `timeout` is expired before woken up or some signals are received,
/// this method will return [`Errno::ETIME`]. /// this method will return [`Errno::ETIME`].
fn pause_timer_timeout(&self, timer_builder: Option<&TimerBuilder>) -> Result<()>; fn pause_timeout<'a>(&self, timeout: impl Into<TimeoutExt<'a>>) -> Result<()>;
} }
impl Pause for Waiter { impl Pause for Waiter {
fn pause_until_or_timer_timeout_opt<F, R>( fn pause_until_or_timeout_impl<F, R>(
&self, &self,
mut cond: F, cond: F,
timer_builder: Option<&TimerBuilder>, timeout: Option<&ManagedTimeout>,
) -> Result<R> ) -> Result<R>
where where
F: FnMut() -> Option<R>, F: FnMut() -> Option<R>,
{ {
if let Some(res) = cond() { // No fast paths for `Waiter`. If the caller wants a fast path, it should do so _before_
return Ok(res); // the waiter is created.
}
let current_thread = self.task().data().downcast_ref::<Arc<Thread>>(); let current_thread = self.task().data().downcast_ref::<Arc<Thread>>();
@ -123,11 +115,7 @@ impl Pause for Waiter {
.as_ref() .as_ref()
.and_then(|thread| thread.as_posix_thread()) .and_then(|thread| thread.as_posix_thread())
else { else {
if let Some(timer_builder) = timer_builder { return self.wait_until_or_timeout_cancelled(cond, || Ok(()), timeout);
return self.wait_until_or_timer_timeout(cond, timer_builder);
} else {
return self.wait_until_or_cancelled(cond, || Ok(()));
}
}; };
let cancel_cond = || { let cancel_cond = || {
@ -141,19 +129,16 @@ impl Pause for Waiter {
}; };
posix_thread.set_signalled_waker(self.waker()); posix_thread.set_signalled_waker(self.waker());
let res = if let Some(timer_builder) = timer_builder { let res = self.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout);
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(); posix_thread.clear_signalled_waker();
res res
} }
fn pause_timer_timeout(&self, timer_builder: Option<&TimerBuilder>) -> Result<()> { fn pause_timeout<'a>(&self, timeout: impl Into<TimeoutExt<'a>>) -> Result<()> {
let timer = timer_builder.map(|timer_builder| { let timer = timeout.into().check_expired()?.map(|timeout| {
let waker = self.waker(); let waker = self.waker();
timer_builder.fire(move || { timeout.create_timer(move || {
waker.wake_up(); waker.wake_up();
}) })
}); });
@ -184,14 +169,15 @@ impl Pause for Waiter {
} }
impl Pause for WaitQueue { impl Pause for WaitQueue {
fn pause_until_or_timer_timeout_opt<F, R>( fn pause_until_or_timeout_impl<F, R>(
&self, &self,
mut cond: F, mut cond: F,
timer_builder: Option<&TimerBuilder>, timeout: Option<&ManagedTimeout>,
) -> Result<R> ) -> Result<R>
where where
F: FnMut() -> Option<R>, F: FnMut() -> Option<R>,
{ {
// Fast path:
if let Some(res) = cond() { if let Some(res) = cond() {
return Ok(res); return Ok(res);
} }
@ -201,11 +187,11 @@ impl Pause for WaitQueue {
self.enqueue(waiter.waker()); self.enqueue(waiter.waker());
cond() 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<()> { fn pause_timeout<'a>(&self, _timeout: impl Into<TimeoutExt<'a>>) -> Result<()> {
panic!("`pause_timer_timeout` can only be used on `Waiter`"); panic!("`pause_timeout` can only be used on `Waiter`");
} }
} }

View File

@ -209,11 +209,7 @@ impl EventCounter {
} }
}; };
if let Some(timeout) = timeout { waiter.pause_until_or_timeout(cond, timeout)
waiter.pause_until_or_timeout(cond, timeout)
} else {
waiter.pause_until(cond)
}
} }
pub fn write(&self) { pub fn write(&self) {

View File

@ -14,7 +14,7 @@ use crate::{
clocks::{MonotonicClock, RealTimeClock}, clocks::{MonotonicClock, RealTimeClock},
timer::Timeout, timer::Timeout,
timespec_t, timespec_t,
wait::TimerBuilder, wait::ManagedTimeout,
}, },
}; };
@ -40,7 +40,7 @@ pub fn sys_futex(
Ok(val as usize) Ok(val as usize)
}; };
let get_futex_timer_builder = |timeout_addr: Vaddr| -> Result<Option<TimerBuilder<'static>>> { let get_futex_timeout = |timeout_addr: Vaddr| -> Result<Option<ManagedTimeout<'static>>> {
if timeout_addr == 0 { if timeout_addr == 0 {
return Ok(None); return Ok(None);
} }
@ -78,7 +78,7 @@ pub fn sys_futex(
MonotonicClock::timer_manager() MonotonicClock::timer_manager()
}; };
Ok(Some(TimerBuilder::new_with_timer_manager( Ok(Some(ManagedTimeout::new_with_manager(
timeout, timeout,
timer_manager, timer_manager,
))) )))
@ -91,15 +91,15 @@ pub fn sys_futex(
}; };
let res = match futex_op { let res = match futex_op {
FutexOp::FUTEX_WAIT => { FutexOp::FUTEX_WAIT => {
let timer_builder = get_futex_timer_builder(utime_addr)?; let timeout = get_futex_timeout(utime_addr)?;
futex_wait(futex_addr as _, futex_val as _, timer_builder, ctx, pid).map(|_| 0) futex_wait(futex_addr as _, futex_val as _, timeout, ctx, pid).map(|_| 0)
} }
FutexOp::FUTEX_WAIT_BITSET => { FutexOp::FUTEX_WAIT_BITSET => {
let timer_builder = get_futex_timer_builder(utime_addr)?; let timeout = get_futex_timeout(utime_addr)?;
futex_wait_bitset( futex_wait_bitset(
futex_addr as _, futex_addr as _,
futex_val as _, futex_val as _,
timer_builder, timeout,
bitset as _, bitset as _,
ctx, ctx,
pid, pid,

View File

@ -12,7 +12,7 @@ use crate::{
clocks::{BootTimeClock, MonotonicClock, RealTimeClock}, clocks::{BootTimeClock, MonotonicClock, RealTimeClock},
timer::Timeout, timer::Timeout,
timespec_t, timespec_t,
wait::TimerBuilder, wait::ManagedTimeout,
TIMER_ABSTIME, TIMER_ABSTIME,
}, },
}; };
@ -109,10 +109,11 @@ fn do_clock_nanosleep(
} }
}; };
let timer_builder = let res = waiter.pause_until_or_timeout(
TimerBuilder::new_with_timer_manager(Timeout::After(duration), timer_manager); || None,
ManagedTimeout::new_with_manager(Timeout::After(duration), timer_manager),
);
let res = waiter.pause_until_or_timer_timeout(|| None, &timer_builder);
match res { match res {
Err(e) if e.error() == Errno::ETIME => Ok(SyscallReturn::Return(0)), Err(e) if e.error() == Errno::ETIME => Ok(SyscallReturn::Return(0)),
Err(e) if e.error() == Errno::EINTR => { Err(e) if e.error() == Errno::EINTR => {

View File

@ -12,23 +12,18 @@ pub trait WaitTimeout {
/// Waits until some condition returns `Some(_)`, or a given timeout is reached. If /// Waits until some condition returns `Some(_)`, or a given timeout is reached. If
/// the condition does not becomes `Some(_)` before the timeout is reached, /// the condition does not becomes `Some(_)` before the timeout is reached,
/// this function will return `ETIME` error. /// this function will return `ETIME` error.
fn wait_until_or_timeout<F, R>(&self, cond: F, timeout: &Duration) -> Result<R> fn wait_until_or_timeout<'a, F, T, R>(&self, mut cond: F, timeout: T) -> Result<R>
where where
F: FnMut() -> Option<R>, F: FnMut() -> Option<R>,
T: Into<TimeoutExt<'a>>,
{ {
let timer_builder = TimerBuilder::new(timeout); let timeout = timeout.into();
self.wait_until_or_timer_timeout(cond, &timer_builder) let timeout_inner = match timeout.check_expired() {
} Ok(inner) => inner,
Err(err) => return cond().ok_or(err),
};
/// Similar to [`WaitTimeout::wait_until_or_timeout`]. self.wait_until_or_timeout_cancelled(cond, || Ok(()), timeout_inner)
///
/// 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<F, R>(&self, cond: F, timer_builder: &TimerBuilder) -> Result<R>
where
F: FnMut() -> Option<R>,
{
self.wait_until_or_timer_timeout_cancelled(cond, || Ok(()), timer_builder)
} }
/// Waits until some condition returns `Some(_)`, or be cancelled due to /// 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` /// does not becomes `Some(_)` before the timeout is reached or `cancel_cond`
/// returns `Err`, this function will return corresponding `Err`. /// returns `Err`, this function will return corresponding `Err`.
#[doc(hidden)] #[doc(hidden)]
fn wait_until_or_timer_timeout_cancelled<F, R, FCancel>( fn wait_until_or_timeout_cancelled<F, R, FCancel>(
&self, &self,
cond: F, cond: F,
cancel_cond: FCancel, cancel_cond: FCancel,
timer_builder: &TimerBuilder, timeout: Option<&ManagedTimeout>,
) -> Result<R> ) -> Result<R>
where where
F: FnMut() -> Option<R>, F: FnMut() -> Option<R>,
FCancel: Fn() -> Result<()>; FCancel: Fn() -> Result<()>;
} }
/// A helper structure to build timers from a specified timeout and timer manager. /// A timeout with extended semantics.
pub struct TimerBuilder<'a> { pub enum TimeoutExt<'a> {
timeout: Timeout, /// The timeout will never fire.
timer_manager: &'a Arc<TimerManager>, Never,
/// The timeout will expire later according to [`ManagedTimeout`].
At(ManagedTimeout<'a>),
} }
impl<'a> TimerBuilder<'a> { impl<'a> TimeoutExt<'a> {
/// Creates a new `TimerBuilder` against the default JIFFIES clock. /// Checks whether the timeout is expired.
pub fn new(timeout: &Duration) -> Self { ///
let timeout = Timeout::After(*timeout); /// This method will return:
let jiffies_timer_manager = JIFFIES_TIMER_MANAGER.get().unwrap(); /// - `Ok(Some(_))` if the timeout isn't expired but it may be expired later.
Self::new_with_timer_manager(timeout, jiffies_timer_manager) /// - `Ok(None)` if the timeout will never be expired.
} /// - `Err(ETIME)` if the timeout is expired.
pub fn check_expired(&self) -> Result<Option<&ManagedTimeout<'a>>> {
/// Creates a new `TimerBuilder` with given timer manager. match self {
pub const fn new_with_timer_manager( TimeoutExt::At(inner) if inner.is_expired() => {
timeout: Timeout, return_errno_with_message!(Errno::ETIME, "the time limit is reached")
timer_manager: &'a Arc<TimerManager>, }
) -> Self { TimeoutExt::At(inner) => Ok(Some(inner)),
Self { TimeoutExt::Never => Ok(None),
timeout,
timer_manager,
} }
} }
}
/// Returns the timeout impl From<&Duration> for TimeoutExt<'_> {
pub const fn timeout(&self) -> &Timeout { fn from(value: &Duration) -> Self {
&self.timeout Self::At(ManagedTimeout::new(*value))
}
}
impl From<Option<&Duration>> for TimeoutExt<'_> {
fn from(value: Option<&Duration>) -> Self {
match value {
Some(duration) => duration.into(),
None => Self::Never,
}
}
}
impl<'a> From<ManagedTimeout<'a>> for TimeoutExt<'a> {
fn from(value: ManagedTimeout<'a>) -> Self {
Self::At(value)
}
}
impl<'a> From<Option<ManagedTimeout<'a>>> for TimeoutExt<'a> {
fn from(value: Option<ManagedTimeout<'a>>) -> 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<TimerManager>,
}
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 { /// Creates a new `ManagedTimeout` with the given timer manager.
self.timer_manager.is_expired_timeout(&self.timeout) pub const fn new_with_manager(timeout: Timeout, manager: &'a Arc<TimerManager>) -> Self {
Self { timeout, manager }
} }
/// Builds and sets a timer, /// Returns weather the timeout is expired.
/// which will trigger `callback` when `self.timeout()` is reached. pub fn is_expired(&self) -> bool {
pub fn fire<F>(&self, callback: F) -> Arc<Timer> self.manager.is_expired_timeout(&self.timeout)
}
/// Creates a timer for the timeout.
pub fn create_timer<F>(&self, callback: F) -> Arc<Timer>
where where
F: Fn() + Send + Sync + 'static, 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.set_timeout(self.timeout.clone());
timer timer
} }
} }
impl WaitTimeout for Waiter { impl WaitTimeout for Waiter {
fn wait_until_or_timer_timeout_cancelled<F, R, FCancel>( fn wait_until_or_timeout_cancelled<F, R, FCancel>(
&self, &self,
mut cond: F, cond: F,
cancel_cond: FCancel, cancel_cond: FCancel,
timer_builder: &TimerBuilder, timeout: Option<&ManagedTimeout>,
) -> Result<R> ) -> Result<R>
where where
F: FnMut() -> Option<R>, F: FnMut() -> Option<R>,
FCancel: Fn() -> Result<()>, FCancel: Fn() -> Result<()>,
{ {
if timer_builder.is_expired() { // No fast paths for `Waiter`. If the caller wants a fast path, it should do so _before_
return cond() // the waiter is created.
.ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached"));
}
if let Some(res) = cond() { let timer = timeout.map(|timeout| {
return Ok(res); let waker = self.waker();
} timeout.create_timer(move || {
waker.wake_up();
let waker = self.waker(); })
let timer = timer_builder.fire(move || {
waker.wake_up();
}); });
let timeout_cond = { let cancel_cond = {
let timer = timer.clone(); 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 = || { move || {
timeout_cond()?; if timer
cancel_cond() .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); let res = self.wait_until_or_cancelled(cond, cancel_cond);
// If `res` is not `ETIME` error, then the timeout may not have been expired. // If `res` is not `ETIME` error, then the timeout may not have been expired.
// We cancel it manually. // We cancel it manually.
if !res if let Some(timer) = timer
.as_ref() && !res
.is_err_and(|e: &Error| e.error() == Errno::ETIME) .as_ref()
.is_err_and(|e: &Error| e.error() == Errno::ETIME)
{ {
timer.cancel(); timer.cancel();
} }
@ -153,21 +186,17 @@ impl WaitTimeout for Waiter {
} }
impl WaitTimeout for WaitQueue { impl WaitTimeout for WaitQueue {
fn wait_until_or_timer_timeout_cancelled<F, R, FCancel>( fn wait_until_or_timeout_cancelled<F, R, FCancel>(
&self, &self,
mut cond: F, mut cond: F,
cancel_cond: FCancel, cancel_cond: FCancel,
timer_builder: &TimerBuilder, timeout: Option<&ManagedTimeout>,
) -> Result<R> ) -> Result<R>
where where
F: FnMut() -> Option<R>, F: FnMut() -> Option<R>,
FCancel: Fn() -> Result<()>, FCancel: Fn() -> Result<()>,
{ {
if timer_builder.is_expired() { // Fast path:
return cond()
.ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached"));
}
if let Some(res) = cond() { if let Some(res) = cond() {
return Ok(res); return Ok(res);
} }
@ -177,6 +206,6 @@ impl WaitTimeout for WaitQueue {
self.enqueue(waiter.waker()); self.enqueue(waiter.waker());
cond() cond()
}; };
waiter.wait_until_or_timer_timeout_cancelled(cond, cancel_cond, timer_builder) waiter.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout)
} }
} }