1use std::fmt;
2use std::iter::once;
3use std::sync::Arc;
4
5use crate::config::Timeouts;
6use crate::transport::time::{Duration, Instant};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum Timeout {
15 Global,
17
18 PerCall,
20
21 Resolve,
23
24 Connect,
26
27 SendRequest,
29
30 #[doc(hidden)]
33 Await100,
34
35 SendBody,
37
38 RecvResponse,
40
41 RecvBody,
43}
44
45impl Timeout {
46 fn preceeding(&self) -> impl Iterator<Item = Timeout> {
48 let prev: &[Timeout] = match self {
49 Timeout::Resolve => &[Timeout::PerCall],
50 Timeout::Connect => &[Timeout::Resolve],
51 Timeout::SendRequest => &[Timeout::Connect],
52 Timeout::Await100 => &[Timeout::SendRequest],
53 Timeout::SendBody => &[Timeout::SendRequest, Timeout::Await100],
54 Timeout::RecvResponse => &[Timeout::SendRequest, Timeout::SendBody],
55 Timeout::RecvBody => &[Timeout::RecvResponse],
56 _ => &[],
57 };
58
59 prev.iter().copied()
60 }
61
62 fn timeouts_to_check(&self) -> impl Iterator<Item = Timeout> {
64 once(*self)
66 .chain(self.preceeding())
67 .chain([Timeout::Global, Timeout::PerCall])
68 }
69
70 fn configured_timeout(&self, timeouts: &Timeouts) -> Option<Duration> {
72 match self {
73 Timeout::Global => timeouts.global,
74 Timeout::PerCall => timeouts.per_call,
75 Timeout::Resolve => timeouts.resolve,
76 Timeout::Connect => timeouts.connect,
77 Timeout::SendRequest => timeouts.send_request,
78 Timeout::Await100 => timeouts.await_100,
79 Timeout::SendBody => timeouts.send_body,
80 Timeout::RecvResponse => timeouts.recv_response,
81 Timeout::RecvBody => timeouts.recv_body,
82 }
83 .map(Into::into)
84 }
85}
86
87#[derive(Default, Debug)]
88pub(crate) struct CallTimings {
89 timeouts: Box<Timeouts>,
90 current_time: CurrentTime,
91 times: Vec<(Timeout, Instant)>,
92}
93
94impl CallTimings {
95 pub(crate) fn new(timeouts: Timeouts, current_time: CurrentTime) -> Self {
96 let mut times = Vec::with_capacity(8);
97
98 let now = current_time.now();
99 times.push((Timeout::Global, now));
100 times.push((Timeout::PerCall, now));
101
102 CallTimings {
103 timeouts: Box::new(timeouts),
104 current_time,
105 times,
106 }
107 }
108
109 pub(crate) fn new_call(mut self) -> CallTimings {
110 self.times.truncate(1); self.times.push((Timeout::PerCall, self.current_time.now()));
112
113 CallTimings {
114 timeouts: self.timeouts,
115 current_time: self.current_time,
116 times: self.times,
117 }
118 }
119
120 pub(crate) fn current_time(&self) -> Arc<dyn Fn() -> Instant + Send + Sync + 'static> {
121 self.current_time.0.clone()
122 }
123
124 pub(crate) fn now(&self) -> Instant {
125 self.current_time.now()
126 }
127
128 pub(crate) fn record_time(&mut self, timeout: Timeout) {
129 assert!(
131 self.time_of(timeout).is_none(),
132 "{:?} recorded more than once",
133 timeout
134 );
135
136 let any_preceeding = timeout
139 .preceeding()
140 .filter_map(|to_check| self.time_of(to_check))
141 .any(|_| true);
142
143 assert!(any_preceeding, "{:?} has no preceeding", timeout);
144
145 self.times.push((timeout, self.current_time.now()));
147 }
148
149 fn time_of(&self, timeout: Timeout) -> Option<Instant> {
150 self.times.iter().find(|x| x.0 == timeout).map(|x| x.1)
151 }
152
153 pub(crate) fn next_timeout(&self, timeout: Timeout) -> NextTimeout {
154 let now = self.now();
155
156 let (reason, at) = timeout
157 .timeouts_to_check()
158 .filter_map(|to_check| {
159 let time = if to_check == timeout {
160 now
161 } else {
162 self.time_of(to_check)?
163 };
164 let timeout = to_check.configured_timeout(&self.timeouts)?;
165 Some((to_check, time + timeout))
166 })
167 .min_by(|a, b| a.1.cmp(&b.1))
168 .unwrap_or((Timeout::Global, Instant::NotHappening));
169
170 let after = at.duration_since(now);
171
172 NextTimeout { after, reason }
173 }
174}
175
176#[derive(Clone)]
177pub(crate) struct CurrentTime(Arc<dyn Fn() -> Instant + Send + Sync + 'static>);
178
179impl CurrentTime {
180 pub(crate) fn now(&self) -> Instant {
181 self.0()
182 }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
187pub struct NextTimeout {
188 pub after: Duration,
190 pub reason: Timeout,
192}
193
194impl NextTimeout {
195 pub fn not_zero(&self) -> Option<Duration> {
199 if self.after.is_not_happening() {
200 None
201 } else if self.after.is_zero() {
202 Some(Duration::from_secs(1))
203 } else {
204 Some(self.after)
205 }
206 }
207}
208
209impl fmt::Debug for CurrentTime {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 f.debug_tuple("CurrentTime").finish()
212 }
213}
214
215impl Default for CurrentTime {
216 fn default() -> Self {
217 Self(Arc::new(Instant::now))
218 }
219}
220
221impl fmt::Display for Timeout {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 let r = match self {
224 Timeout::Global => "global",
225 Timeout::PerCall => "per call",
226 Timeout::Resolve => "resolve",
227 Timeout::Connect => "connect",
228 Timeout::SendRequest => "send request",
229 Timeout::SendBody => "send body",
230 Timeout::Await100 => "await 100",
231 Timeout::RecvResponse => "receive response",
232 Timeout::RecvBody => "receive body",
233 };
234 write!(f, "{}", r)
235 }
236}