1use std::time::{Duration, Instant};
5
6pub struct TimeoutManager {
7 start_time: Instant,
8 timeout: Duration,
9}
10
11impl TimeoutManager {
12 const MINIMUM_REAP_TIME: Duration = Duration::from_millis(160);
14
15 pub fn new(timeout: Duration) -> Self {
16 Self {
17 start_time: Instant::now(),
18 timeout,
19 }
20 }
21
22 pub fn remaining(&self) -> Duration {
23 let elapsed = self.start_time.elapsed();
25 if elapsed >= self.timeout {
26 Self::MINIMUM_REAP_TIME
27 } else {
28 (self.timeout - elapsed).max(Self::MINIMUM_REAP_TIME)
29 }
30 }
31
32 pub fn elapsed(&self) -> Duration {
33 self.start_time.elapsed()
34 }
35
36 pub fn timeout(&self) -> Duration {
37 self.timeout
38 }
39}
40
41impl std::fmt::Debug for TimeoutManager {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.debug_struct("TimeoutManager")
44 .field("start_time", &self.start_time)
45 .field("elapsed", &self.elapsed())
46 .field("timeout", &self.timeout)
47 .field("remaining", &self.remaining())
48 .finish()
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn test_timeout_manager_new() {
58 let timeout = Duration::from_secs(5);
59 let manager = TimeoutManager::new(timeout);
60
61 assert_eq!(manager.timeout(), timeout);
62 assert!(manager.elapsed() < Duration::from_millis(100)); assert!(manager.remaining() >= TimeoutManager::MINIMUM_REAP_TIME);
64 }
65
66 #[test]
67 fn test_timeout_manager_remaining() {
68 let timeout = Duration::from_millis(100);
69 let manager = TimeoutManager::new(timeout);
70
71 let remaining = manager.remaining();
73 assert!(remaining >= TimeoutManager::MINIMUM_REAP_TIME);
74 std::thread::sleep(Duration::from_millis(10));
78 let remaining_after_sleep = manager.remaining();
79 assert!(remaining_after_sleep >= TimeoutManager::MINIMUM_REAP_TIME);
80 }
81
82 #[test]
83 fn test_timeout_manager_elapsed() {
84 let timeout = Duration::from_secs(1);
85 let manager = TimeoutManager::new(timeout);
86
87 assert!(manager.elapsed() < Duration::from_millis(100));
89
90 std::thread::sleep(Duration::from_millis(10));
92 let elapsed = manager.elapsed();
93 assert!(elapsed >= Duration::from_millis(10));
94
95 #[cfg(not(miri))] assert!(elapsed < Duration::from_millis(100)); }
98
99 #[test]
100 fn test_timeout_manager_minimum_reap_time() {
101 let timeout = Duration::from_millis(50); let manager = TimeoutManager::new(timeout);
103
104 assert_eq!(manager.remaining(), TimeoutManager::MINIMUM_REAP_TIME);
106 }
107
108 #[test]
109 fn test_timeout_manager_debug() {
110 let timeout = Duration::from_secs(1);
111 let manager = TimeoutManager::new(timeout);
112
113 let debug_str = format!("{manager:?}");
114
115 assert!(debug_str.contains("TimeoutManager"));
117 assert!(debug_str.contains("start_time"));
118 assert!(debug_str.contains("elapsed"));
119 assert!(debug_str.contains("timeout"));
120 assert!(debug_str.contains("remaining"));
121 }
122
123 #[test]
124 fn test_timeout_manager_timeout_exceeded() {
125 let timeout = Duration::from_millis(10);
126 let manager = TimeoutManager::new(timeout);
127
128 std::thread::sleep(Duration::from_millis(50));
130
131 assert!(manager.elapsed() > timeout);
133
134 let remaining = manager.remaining();
136 assert_eq!(remaining, TimeoutManager::MINIMUM_REAP_TIME);
137 }
138}