2024-04-30 03:21:56 +00:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
|
|
|
|
|
use core::time::Duration;
|
|
|
|
|
|
2024-06-19 08:18:39 +00:00
|
|
|
use ostd::sync::{WaitQueue, Waiter};
|
2024-04-30 03:21:56 +00:00
|
|
|
|
2024-08-01 03:17:49 +00:00
|
|
|
use super::{clocks::JIFFIES_TIMER_MANAGER, timer::Timeout, Timer, TimerManager};
|
2024-09-14 02:13:14 +00:00
|
|
|
use crate::prelude::*;
|
2024-04-30 03:21:56 +00:00
|
|
|
|
2024-09-14 02:13:14 +00:00
|
|
|
/// A trait that provide the timeout related function for [`Waiter`] and [`WaitQueue`]`.
|
2024-04-30 03:21:56 +00:00
|
|
|
pub trait WaitTimeout {
|
2024-09-14 02:13:14 +00:00
|
|
|
/// 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<F, R>(&self, cond: F, timeout: &Duration) -> Result<R>
|
2024-04-30 03:21:56 +00:00
|
|
|
where
|
2024-09-14 02:13:14 +00:00
|
|
|
F: FnMut() -> Option<R>,
|
|
|
|
|
{
|
2024-08-01 03:17:49 +00:00
|
|
|
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<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)
|
2024-09-14 02:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Waits until some condition returns `Some(_)`, or be cancelled due to
|
|
|
|
|
/// reaching the timeout or the inputted cancel condition. If the condition
|
|
|
|
|
/// does not becomes `Some(_)` before the timeout is reached or `cancel_cond`
|
|
|
|
|
/// returns `Err`, this function will return corresponding `Err`.
|
|
|
|
|
#[doc(hidden)]
|
2024-08-01 03:17:49 +00:00
|
|
|
fn wait_until_or_timer_timeout_cancelled<F, R, FCancel>(
|
2024-09-14 02:13:14 +00:00
|
|
|
&self,
|
|
|
|
|
cond: F,
|
|
|
|
|
cancel_cond: FCancel,
|
2024-08-01 03:17:49 +00:00
|
|
|
timer_builder: &TimerBuilder,
|
2024-09-14 02:13:14 +00:00
|
|
|
) -> Result<R>
|
|
|
|
|
where
|
|
|
|
|
F: FnMut() -> Option<R>,
|
|
|
|
|
FCancel: Fn() -> Result<()>;
|
2024-04-30 03:21:56 +00:00
|
|
|
}
|
|
|
|
|
|
2024-08-01 03:17:49 +00:00
|
|
|
/// A helper structure to build timers from a specified timeout and timer manager.
|
|
|
|
|
pub struct TimerBuilder<'a> {
|
|
|
|
|
timeout: Timeout,
|
|
|
|
|
timer_manager: &'a Arc<TimerManager>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<TimerManager>,
|
|
|
|
|
) -> 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<F>(&self, callback: F) -> Arc<Timer>
|
|
|
|
|
where
|
|
|
|
|
F: Fn() + Send + Sync + 'static,
|
|
|
|
|
{
|
|
|
|
|
let timer = self.timer_manager.create_timer(callback);
|
|
|
|
|
timer.set_timeout(self.timeout.clone());
|
|
|
|
|
timer
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-14 02:13:14 +00:00
|
|
|
impl WaitTimeout for Waiter {
|
2024-08-01 03:17:49 +00:00
|
|
|
fn wait_until_or_timer_timeout_cancelled<F, R, FCancel>(
|
2024-09-14 02:13:14 +00:00
|
|
|
&self,
|
|
|
|
|
mut cond: F,
|
|
|
|
|
cancel_cond: FCancel,
|
2024-08-01 03:17:49 +00:00
|
|
|
timer_builder: &TimerBuilder,
|
2024-09-14 02:13:14 +00:00
|
|
|
) -> Result<R>
|
2024-04-30 03:21:56 +00:00
|
|
|
where
|
|
|
|
|
F: FnMut() -> Option<R>,
|
2024-09-14 02:13:14 +00:00
|
|
|
FCancel: Fn() -> Result<()>,
|
2024-04-30 03:21:56 +00:00
|
|
|
{
|
2024-08-01 03:17:49 +00:00
|
|
|
if timer_builder.is_expired() {
|
2024-09-14 02:13:14 +00:00
|
|
|
return cond()
|
|
|
|
|
.ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached"));
|
2024-05-11 09:14:24 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-30 03:21:56 +00:00
|
|
|
if let Some(res) = cond() {
|
2024-09-14 02:13:14 +00:00
|
|
|
return Ok(res);
|
2024-04-30 03:21:56 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-14 02:13:14 +00:00
|
|
|
let waker = self.waker();
|
2024-08-01 03:17:49 +00:00
|
|
|
let timer = timer_builder.fire(move || {
|
2024-05-11 09:14:24 +00:00
|
|
|
waker.wake_up();
|
|
|
|
|
});
|
2024-04-30 03:21:56 +00:00
|
|
|
|
2024-09-14 02:13:14 +00:00
|
|
|
let timeout_cond = {
|
2024-08-01 03:17:49 +00:00
|
|
|
let timer = timer.clone();
|
2024-09-14 02:13:14 +00:00
|
|
|
move || {
|
2024-08-01 03:17:49 +00:00
|
|
|
if timer.remain() != Duration::ZERO {
|
2024-09-14 02:13:14 +00:00
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
Err(Error::with_message(
|
|
|
|
|
Errno::ETIME,
|
|
|
|
|
"the time limit is reached",
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let cancel_cond = || {
|
|
|
|
|
timeout_cond()?;
|
|
|
|
|
cancel_cond()
|
2024-05-11 09:14:24 +00:00
|
|
|
};
|
2024-04-30 03:21:56 +00:00
|
|
|
|
2024-09-14 02:13:14 +00:00
|
|
|
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)
|
|
|
|
|
{
|
2024-08-01 03:17:49 +00:00
|
|
|
timer.cancel();
|
2024-04-30 03:21:56 +00:00
|
|
|
}
|
2024-05-11 09:14:24 +00:00
|
|
|
|
|
|
|
|
res
|
2024-04-30 03:21:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
2024-09-14 02:13:14 +00:00
|
|
|
|
|
|
|
|
impl WaitTimeout for WaitQueue {
|
2024-08-01 03:17:49 +00:00
|
|
|
fn wait_until_or_timer_timeout_cancelled<F, R, FCancel>(
|
2024-09-14 02:13:14 +00:00
|
|
|
&self,
|
|
|
|
|
mut cond: F,
|
|
|
|
|
cancel_cond: FCancel,
|
2024-08-01 03:17:49 +00:00
|
|
|
timer_builder: &TimerBuilder,
|
2024-09-14 02:13:14 +00:00
|
|
|
) -> Result<R>
|
|
|
|
|
where
|
|
|
|
|
F: FnMut() -> Option<R>,
|
|
|
|
|
FCancel: Fn() -> Result<()>,
|
|
|
|
|
{
|
2024-08-01 03:17:49 +00:00
|
|
|
if timer_builder.is_expired() {
|
2024-09-14 02:13:14 +00:00
|
|
|
return cond()
|
|
|
|
|
.ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(res) = cond() {
|
|
|
|
|
return Ok(res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let (waiter, _) = Waiter::new_pair();
|
|
|
|
|
let cond = || {
|
|
|
|
|
self.enqueue(waiter.waker());
|
|
|
|
|
cond()
|
|
|
|
|
};
|
2024-08-01 03:17:49 +00:00
|
|
|
waiter.wait_until_or_timer_timeout_cancelled(cond, cancel_cond, timer_builder)
|
2024-09-14 02:13:14 +00:00
|
|
|
}
|
|
|
|
|
}
|