43 lines
1.2 KiB
Rust
43 lines
1.2 KiB
Rust
use std::time::{Duration, Instant};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum WaitOutcome {
|
|
Satisfied,
|
|
Timeout,
|
|
DisabledUnknown,
|
|
}
|
|
|
|
/// Wait for a predicate with a deadline. Unknown disabled reasons do not spin
|
|
/// forever — they surface as DisabledUnknown so the caller can re-observe.
|
|
pub fn wait_until<F>(deadline: Duration, mut ready: F) -> WaitOutcome
|
|
where
|
|
F: FnMut() -> Result<bool, &'static str>,
|
|
{
|
|
let start = Instant::now();
|
|
loop {
|
|
match ready() {
|
|
Ok(true) => return WaitOutcome::Satisfied,
|
|
Ok(false) if start.elapsed() >= deadline => return WaitOutcome::Timeout,
|
|
Ok(false) => std::thread::sleep(Duration::from_millis(5)),
|
|
Err(_) => return WaitOutcome::DisabledUnknown,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn deadline_stops_an_unready_condition() {
|
|
let outcome = wait_until(Duration::from_millis(20), || Ok(false));
|
|
assert_eq!(outcome, WaitOutcome::Timeout);
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_disabled_does_not_spin() {
|
|
let outcome = wait_until(Duration::from_secs(5), || Err("unknown"));
|
|
assert_eq!(outcome, WaitOutcome::DisabledUnknown);
|
|
}
|
|
}
|