mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-21 16:33:24 +00:00
Make *_or_timeout
APIs concise
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
10cc85d588
commit
32b8e05cc3
@ -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
|
||||||
|
@ -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`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
@ -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 => {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user