Add wait_until_* functionality to Waiter

This commit is contained in:
Chen Chengjun
2024-09-14 10:13:14 +08:00
committed by Tate, Hongliang Tian
parent 6a5a5b4d3d
commit 1873bb7a3f
5 changed files with 140 additions and 62 deletions

View File

@ -59,7 +59,7 @@ fn spawn_background_poll_thread(iface: Arc<Iface>) {
}
let duration = Duration::from_millis(next_poll_at_ms - now_as_ms);
wait_queue.wait_until_or_timeout(
let _ = wait_queue.wait_until_or_timeout(
// If `iface_ext.next_poll_at_ms()` changes to an earlier time, we will end the
// waiting.
|| (iface_ext.next_poll_at_ms()? < next_poll_at_ms).then_some(()),

View File

@ -164,8 +164,8 @@ impl Condvar {
// Wait until the condition becomes true, we're explicitly woken up, or the timeout elapses.
let res = self.waitqueue.wait_until_or_timeout(cond, &timeout);
match res {
Some(_) => Ok((lock.lock(), false)),
None => {
Ok(()) => Ok((lock.lock(), false)),
Err(_) => {
let mut counter = self.counter.lock();
counter.waiter_count -= 1;
Err(LockErr::Timeout((lock.lock(), true)))

View File

@ -264,7 +264,7 @@ impl Monitor {
for local_pool in worker_pool.local_pools.iter() {
local_pool.set_heartbeat(false);
}
sleep_queue.wait_until_or_timeout(|| -> Option<()> { None }, &sleep_duration);
let _ = sleep_queue.wait_until_or_timeout(|| -> Option<()> { None }, &sleep_duration);
}
}
}

View File

@ -5,48 +5,121 @@ use core::time::Duration;
use ostd::sync::{WaitQueue, Waiter};
use super::{clocks::JIFFIES_TIMER_MANAGER, timer::Timeout};
use crate::prelude::*;
/// A trait that provide the timeout related function for [`WaitQueue`]`.
/// A trait that provide the timeout related function for [`Waiter`] and [`WaitQueue`]`.
pub trait WaitTimeout {
/// Wait until some condition returns `Some(_)`, or a given timeout is reached. If
/// the condition does not becomes `Some(_)` before the timeout is reached, the
/// function will return `None`.
fn wait_until_or_timeout<F, R>(&self, cond: F, timeout: &Duration) -> Option<R>
where
F: FnMut() -> Option<R>;
}
impl WaitTimeout for WaitQueue {
fn wait_until_or_timeout<F, R>(&self, mut cond: F, timeout: &Duration) -> Option<R>
/// 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>
where
F: FnMut() -> Option<R>,
{
self.wait_until_or_timeout_cancelled(cond, || Ok(()), timeout)
}
/// 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)]
fn wait_until_or_timeout_cancelled<F, R, FCancel>(
&self,
cond: F,
cancel_cond: FCancel,
timeout: &Duration,
) -> Result<R>
where
F: FnMut() -> Option<R>,
FCancel: Fn() -> Result<()>;
}
impl WaitTimeout for Waiter {
fn wait_until_or_timeout_cancelled<F, R, FCancel>(
&self,
mut cond: F,
cancel_cond: FCancel,
timeout: &Duration,
) -> Result<R>
where
F: FnMut() -> Option<R>,
FCancel: Fn() -> Result<()>,
{
if *timeout == Duration::ZERO {
return cond();
return cond()
.ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached"));
}
if let Some(res) = cond() {
return Some(res);
return Ok(res);
}
let (waiter, waker) = Waiter::new_pair();
let waker = self.waker();
let jiffies_timer = JIFFIES_TIMER_MANAGER.get().unwrap().create_timer(move || {
waker.wake_up();
});
jiffies_timer.set_timeout(Timeout::After(*timeout));
let cancel_cond = {
let timeout_cond = {
let jiffies_timer = jiffies_timer.clone();
move || jiffies_timer.remain() == Duration::ZERO
move || {
if jiffies_timer.remain() != Duration::ZERO {
Ok(())
} else {
Err(Error::with_message(
Errno::ETIME,
"the time limit is reached",
))
}
}
};
let res = self.wait_until_or_cancelled(cond, waiter, cancel_cond);
// If res is `Some`, then the timeout may not have been expired. We cancel it manually.
if res.is_some() {
let cancel_cond = || {
timeout_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.
// We cancel it manually.
if !res
.as_ref()
.is_err_and(|e: &Error| e.error() == Errno::ETIME)
{
jiffies_timer.cancel();
}
res
}
}
impl WaitTimeout for WaitQueue {
fn wait_until_or_timeout_cancelled<F, R, FCancel>(
&self,
mut cond: F,
cancel_cond: FCancel,
timeout: &Duration,
) -> Result<R>
where
F: FnMut() -> Option<R>,
FCancel: Fn() -> Result<()>,
{
if *timeout == Duration::ZERO {
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()
};
waiter.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout)
}
}