use std::time::{Duration, Instant};
pub struct TimeoutManager {
start_time: Instant,
timeout: Duration,
}
impl TimeoutManager {
const MINIMUM_REAP_TIME: Duration = Duration::from_millis(160);
pub fn new(timeout: Duration) -> Self {
Self {
start_time: Instant::now(),
timeout,
}
}
pub fn remaining(&self) -> Duration {
let elapsed = self.start_time.elapsed();
if elapsed >= self.timeout {
Self::MINIMUM_REAP_TIME
} else {
(self.timeout - elapsed).max(Self::MINIMUM_REAP_TIME)
}
}
pub fn elapsed(&self) -> Duration {
self.start_time.elapsed()
}
pub fn timeout(&self) -> Duration {
self.timeout
}
}
impl std::fmt::Debug for TimeoutManager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TimeoutManager")
.field("start_time", &self.start_time)
.field("elapsed", &self.elapsed())
.field("timeout", &self.timeout)
.field("remaining", &self.remaining())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timeout_manager_new() {
let timeout = Duration::from_secs(5);
let manager = TimeoutManager::new(timeout);
assert_eq!(manager.timeout(), timeout);
assert!(manager.elapsed() < Duration::from_millis(100)); assert!(manager.remaining() >= TimeoutManager::MINIMUM_REAP_TIME);
}
#[test]
fn test_timeout_manager_remaining() {
let timeout = Duration::from_millis(100);
let manager = TimeoutManager::new(timeout);
let remaining = manager.remaining();
assert!(remaining >= TimeoutManager::MINIMUM_REAP_TIME);
std::thread::sleep(Duration::from_millis(10));
let remaining_after_sleep = manager.remaining();
assert!(remaining_after_sleep >= TimeoutManager::MINIMUM_REAP_TIME);
}
#[test]
fn test_timeout_manager_elapsed() {
let timeout = Duration::from_secs(1);
let manager = TimeoutManager::new(timeout);
assert!(manager.elapsed() < Duration::from_millis(100));
std::thread::sleep(Duration::from_millis(10));
let elapsed = manager.elapsed();
assert!(elapsed >= Duration::from_millis(10));
#[cfg(not(miri))] assert!(elapsed < Duration::from_millis(100)); }
#[test]
fn test_timeout_manager_minimum_reap_time() {
let timeout = Duration::from_millis(50); let manager = TimeoutManager::new(timeout);
assert_eq!(manager.remaining(), TimeoutManager::MINIMUM_REAP_TIME);
}
#[test]
fn test_timeout_manager_debug() {
let timeout = Duration::from_secs(1);
let manager = TimeoutManager::new(timeout);
let debug_str = format!("{manager:?}");
assert!(debug_str.contains("TimeoutManager"));
assert!(debug_str.contains("start_time"));
assert!(debug_str.contains("elapsed"));
assert!(debug_str.contains("timeout"));
assert!(debug_str.contains("remaining"));
}
#[test]
fn test_timeout_manager_timeout_exceeded() {
let timeout = Duration::from_millis(10);
let manager = TimeoutManager::new(timeout);
std::thread::sleep(Duration::from_millis(50));
assert!(manager.elapsed() > timeout);
let remaining = manager.remaining();
assert_eq!(remaining, TimeoutManager::MINIMUM_REAP_TIME);
}
}