pingora_timeout/
fast_timeout.rs1use super::timer::*;
28use super::*;
29use once_cell::sync::Lazy;
30use std::sync::atomic::{AtomicU64, Ordering};
31use std::sync::Arc;
32
33static TIMER_MANAGER: Lazy<Arc<TimerManager>> = Lazy::new(|| {
34 let tm = Arc::new(TimerManager::new());
35 check_clock_thread(&tm);
36 tm
37});
38
39pub const DEFAULT_FAST_TIMEOUT_TO_TOKIO_THRESHOLD: Duration = Duration::from_secs(15 * 60);
41const DEFAULT_FAST_TIMEOUT_TO_TOKIO_THRESHOLD_SECS: u64 =
42 DEFAULT_FAST_TIMEOUT_TO_TOKIO_THRESHOLD.as_secs();
43const FAST_TIMEOUT_TO_TOKIO_DISABLED: u64 = u64::MAX;
44
45static FAST_TIMEOUT_TO_TOKIO_THRESHOLD_SECS: AtomicU64 =
46 AtomicU64::new(DEFAULT_FAST_TIMEOUT_TO_TOKIO_THRESHOLD_SECS);
47
48fn check_clock_thread(tm: &Arc<TimerManager>) {
49 if tm.should_i_start_clock() {
50 std::thread::Builder::new()
51 .name("Timer thread".into())
52 .spawn(|| TIMER_MANAGER.clock_thread())
53 .unwrap();
54 }
55}
56
57fn should_use_tokio_timeout(duration: Duration) -> bool {
58 let threshold_secs = FAST_TIMEOUT_TO_TOKIO_THRESHOLD_SECS.load(Ordering::Relaxed);
59 threshold_secs != FAST_TIMEOUT_TO_TOKIO_DISABLED && duration.as_secs() > threshold_secs
60}
61
62pub fn set_fast_timeout_to_tokio_threshold(threshold: Option<Duration>) {
70 FAST_TIMEOUT_TO_TOKIO_THRESHOLD_SECS.store(
71 threshold.map_or(FAST_TIMEOUT_TO_TOKIO_DISABLED, |threshold| {
72 threshold.as_secs()
73 }),
74 Ordering::Relaxed,
75 );
76}
77
78pub fn fast_timeout_to_tokio_threshold() -> Option<Duration> {
80 match FAST_TIMEOUT_TO_TOKIO_THRESHOLD_SECS.load(Ordering::Relaxed) {
81 FAST_TIMEOUT_TO_TOKIO_DISABLED => None,
82 threshold_secs => Some(Duration::from_secs(threshold_secs)),
83 }
84}
85
86pub struct FastTimeout(Duration);
90
91impl ToTimeout for FastTimeout {
92 fn timeout(&self) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>> {
93 Box::pin(TIMER_MANAGER.register_timer(self.0).poll())
94 }
95
96 fn create(d: Duration) -> Self {
97 FastTimeout(d)
98 }
99}
100
101enum TimeoutKind {
102 Fast,
103 Tokio,
104}
105
106pub struct FastOrTokioTimeout {
110 duration: Duration,
111 kind: TimeoutKind,
112}
113
114impl FastOrTokioTimeout {
115 fn use_tokio(&self) -> bool {
116 matches!(self.kind, TimeoutKind::Tokio)
117 }
118}
119
120impl ToTimeout for FastOrTokioTimeout {
121 fn timeout(&self) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>> {
122 match self.kind {
123 TimeoutKind::Fast => FastTimeout::create(self.duration).timeout(),
124 TimeoutKind::Tokio => TokioTimeout::create(self.duration).timeout(),
125 }
126 }
127
128 fn create(duration: Duration) -> Self {
129 Self {
130 duration,
131 kind: if should_use_tokio_timeout(duration) {
132 TimeoutKind::Tokio
133 } else {
134 TimeoutKind::Fast
135 },
136 }
137 }
138}
139
140pub fn fast_timeout<T>(duration: Duration, future: T) -> Timeout<T, FastOrTokioTimeout>
142where
143 T: Future,
144{
145 let callback = FastOrTokioTimeout::create(duration);
146 if !callback.use_tokio() {
147 check_clock_thread(&TIMER_MANAGER);
148 }
149
150 Timeout::new_with_callback(future, callback)
151}
152
153pub async fn fast_sleep(duration: Duration) {
155 check_clock_thread(&TIMER_MANAGER);
156 TIMER_MANAGER.register_timer(duration).poll().await
157}
158
159pub fn pause_for_fork() {
166 TIMER_MANAGER.pause_for_fork();
167}
168
169pub fn unpause() {
173 TIMER_MANAGER.unpause();
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[tokio::test]
181 async fn test_timeout() {
182 let fut = tokio_sleep(Duration::from_secs(1000));
183 let to = fast_timeout(Duration::from_secs(1), fut);
184 assert!(to.await.is_err())
185 }
186
187 #[tokio::test]
188 async fn test_instantly_return() {
189 let fut = async { 1 };
190 let to = fast_timeout(Duration::from_secs(1), fut);
191 assert_eq!(to.await.unwrap(), 1)
192 }
193
194 #[tokio::test]
195 async fn test_delayed_return() {
196 let fut = async {
197 tokio_sleep(Duration::from_secs(1)).await;
198 1
199 };
200 let to = fast_timeout(Duration::from_secs(60), fut);
201 assert_eq!(to.await.unwrap(), 1)
202 }
203
204 #[tokio::test]
205 async fn test_sleep() {
206 let fut = async {
207 fast_sleep(Duration::from_secs(1)).await;
208 1
209 };
210 let to = fast_timeout(Duration::from_secs(60), fut);
211 assert_eq!(to.await.unwrap(), 1)
212 }
213
214 #[test]
215 fn test_default_fast_timeout_to_tokio_threshold() {
216 assert_eq!(
217 Some(Duration::from_secs(15 * 60)),
218 fast_timeout_to_tokio_threshold()
219 );
220 }
221
222 #[test]
223 fn test_fast_timeout_uses_fast_below_threshold() {
224 let callback = FastOrTokioTimeout::create(Duration::from_secs(1));
225 assert!(!callback.use_tokio());
226 }
227
228 #[test]
229 fn test_fast_timeout_uses_fast_at_threshold() {
230 let callback = FastOrTokioTimeout::create(fast_timeout_to_tokio_threshold().unwrap());
231 assert!(!callback.use_tokio());
232 }
233
234 #[test]
235 fn test_fast_timeout_uses_tokio_above_threshold() {
236 let callback = FastOrTokioTimeout::create(
237 fast_timeout_to_tokio_threshold().unwrap() + Duration::from_secs(1),
238 );
239 assert!(callback.use_tokio());
240 }
241}