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 now(&self) -> Instant {
121 self.current_time.now()
122 }
123
124 pub(crate) fn record_time(&mut self, timeout: Timeout) {
125 assert!(
127 self.time_of(timeout).is_none(),
128 "{:?} recorded more than once",
129 timeout
130 );
131
132 let any_preceeding = timeout
135 .preceeding()
136 .filter_map(|to_check| self.time_of(to_check))
137 .any(|_| true);
138
139 assert!(any_preceeding, "{:?} has no preceeding", timeout);
140
141 self.times.push((timeout, self.current_time.now()));
143 }
144
145 fn time_of(&self, timeout: Timeout) -> Option<Instant> {
146 self.times.iter().find(|x| x.0 == timeout).map(|x| x.1)
147 }
148
149 pub(crate) fn next_timeout(&self, timeout: Timeout) -> NextTimeout {
150 let now = self.now();
151
152 let (reason, at) = timeout
153 .timeouts_to_check()
154 .filter_map(|to_check| {
155 let time = if to_check == timeout {
156 now
157 } else {
158 self.time_of(to_check)?
159 };
160 let timeout = to_check.configured_timeout(&self.timeouts)?;
161 Some((to_check, time + timeout))
162 })
163 .min_by(|a, b| a.1.cmp(&b.1))
164 .unwrap_or((Timeout::Global, Instant::NotHappening));
165
166 let after = at.duration_since(now);
167
168 NextTimeout { after, reason }
169 }
170}
171
172#[derive(Clone)]
173pub(crate) struct CurrentTime(Arc<dyn Fn() -> Instant + Send + Sync + 'static>);
174
175impl CurrentTime {
176 pub(crate) fn now(&self) -> Instant {
177 self.0()
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub struct NextTimeout {
184 pub after: Duration,
186 pub reason: Timeout,
188}
189
190impl NextTimeout {
191 pub fn not_zero(&self) -> Option<Duration> {
195 if self.after.is_not_happening() {
196 None
197 } else if self.after.is_zero() {
198 Some(Duration::from_secs(1))
199 } else {
200 Some(self.after)
201 }
202 }
203}
204
205impl fmt::Debug for CurrentTime {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 f.debug_tuple("CurrentTime").finish()
208 }
209}
210
211impl Default for CurrentTime {
212 fn default() -> Self {
213 Self(Arc::new(Instant::now))
214 }
215}
216
217impl fmt::Display for Timeout {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 let r = match self {
220 Timeout::Global => "global",
221 Timeout::PerCall => "per call",
222 Timeout::Resolve => "resolve",
223 Timeout::Connect => "connect",
224 Timeout::SendRequest => "send request",
225 Timeout::SendBody => "send body",
226 Timeout::Await100 => "await 100",
227 Timeout::RecvResponse => "receive response",
228 Timeout::RecvBody => "receive body",
229 };
230 write!(f, "{}", r)
231 }
232}