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