1use identity_hash::{IdentityHashable, IntMap};
2
3use crate::{
4 Instant,
5 connection::qlog::{QlogSink, QlogSinkWithTime},
6};
7
8use super::PathId;
9
10#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
11pub(crate) enum Timer {
12 Conn(ConnTimer),
14 PerPath(PathId, PathTimer),
16}
17
18#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
19pub(crate) enum ConnTimer {
20 Idle = 0,
22 Close = 1,
24 KeyDiscard = 2,
26 KeepAlive = 3,
28 PushNewCid = 4,
30 NoAvailablePath = 5,
35 NatTraversalProbeRetry = 6,
39}
40
41impl ConnTimer {
42 const VALUES: [Self; 7] = [
43 Self::Idle,
44 Self::Close,
45 Self::KeyDiscard,
46 Self::KeepAlive,
47 Self::PushNewCid,
48 Self::NoAvailablePath,
49 Self::NatTraversalProbeRetry,
50 ];
51}
52
53#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
54pub(crate) enum PathTimer {
55 LossDetection = 0,
57 PathIdle = 1,
59 PathValidationFailed = 2,
61 PathChallengeLost = 3,
63 PathKeepAlive = 4,
65 Pacing = 5,
67 MaxAckDelay = 6,
69 PathDrained = 7,
71}
72
73impl PathTimer {
74 pub(super) const VALUES: [Self; 8] = [
75 Self::LossDetection,
76 Self::PathIdle,
77 Self::PathValidationFailed,
78 Self::PathChallengeLost,
79 Self::PathKeepAlive,
80 Self::Pacing,
81 Self::MaxAckDelay,
82 Self::PathDrained,
83 ];
84}
85
86#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
88struct PathIdKey(PathId);
89
90impl IdentityHashable for PathIdKey {}
91
92impl std::hash::Hash for PathIdKey {
93 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
94 state.write_u32(self.0.0);
95 }
96}
97
98#[derive(Debug, Clone, Default)]
102pub(crate) struct TimerTable {
103 generic: [Option<Instant>; ConnTimer::VALUES.len()],
104 path_timers: SmallMap<PathIdKey, PathTimerTable, STACK_TIMERS>,
105}
106
107const STACK_TIMERS: usize = 4;
109
110#[derive(Debug, Clone)]
112struct SmallMap<K, V, const SIZE: usize> {
113 stack: [Option<(K, V)>; SIZE],
114 heap: Option<IntMap<K, V>>,
115}
116
117impl<K, V, const SIZE: usize> Default for SmallMap<K, V, SIZE> {
118 fn default() -> Self {
119 Self {
120 stack: [const { None }; SIZE],
121 heap: None,
122 }
123 }
124}
125
126impl<K, V, const SIZE: usize> SmallMap<K, V, SIZE>
127where
128 K: Eq + std::hash::Hash + IdentityHashable,
129{
130 fn insert(&mut self, key: K, value: V) -> Option<V> {
131 for el in self.stack.iter_mut() {
133 match el {
134 Some((k, v)) => {
135 if *k == key {
136 let old_value = std::mem::replace(v, value);
137 return Some(old_value);
138 }
139 }
140 None => {
141 let old_heap = self.heap.as_mut().and_then(|h| h.remove(&key));
143 *el = Some((key, value));
144
145 return old_heap;
146 }
147 }
148 }
149
150 let heap = self.heap.get_or_insert_default();
152 heap.insert(key, value)
153 }
154
155 #[cfg(test)]
156 fn remove(&mut self, key: &K) -> Option<V> {
157 for el in self.stack.iter_mut() {
158 if let Some((k, _)) = el
159 && key == k
160 {
161 return el.take().map(|(_, v)| v);
162 }
163 }
164
165 self.heap.as_mut().and_then(|h| h.remove(key))
166 }
167
168 fn get(&self, key: &K) -> Option<&V> {
169 for (k, v) in self.stack.iter().filter_map(|v| v.as_ref()) {
170 if k == key {
171 return Some(v);
172 }
173 }
174
175 self.heap.as_ref().and_then(|h| h.get(key))
176 }
177
178 fn get_mut(&mut self, key: &K) -> Option<&mut V> {
179 for (k, v) in self.stack.iter_mut().filter_map(|v| v.as_mut()) {
180 if k == key {
181 return Some(v);
182 }
183 }
184
185 self.heap.as_mut().and_then(|h| h.get_mut(key))
186 }
187
188 #[cfg(test)]
189 fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
190 let a = self
191 .stack
192 .iter()
193 .filter_map(|v| v.as_ref().map(|(k, v)| (k, v)));
194 let b = self.heap.iter().flat_map(|h| h.iter());
195 a.chain(b)
196 }
197
198 fn values(&self) -> impl Iterator<Item = &V> {
199 let a = self.stack.iter().filter_map(|v| v.as_ref().map(|(_, v)| v));
200 let b = self.heap.iter().flat_map(|h| h.values());
201 a.chain(b)
202 }
203
204 fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
205 let a = self
206 .stack
207 .iter_mut()
208 .filter_map(|v| v.as_mut().map(|(k, v)| (&*k, v)));
209 let b = self.heap.iter_mut().flat_map(|h| h.iter_mut());
210 a.chain(b)
211 }
212
213 fn retain<F>(&mut self, mut f: F)
214 where
215 F: FnMut(&K, &mut V) -> bool,
216 {
217 let mut to_remove = [false; SIZE];
218 for (i, el) in self.stack.iter_mut().enumerate() {
219 if let Some((key, value)) = el {
220 to_remove[i] = !f(key, value);
221 }
222 }
223 for (i, to_remove) in to_remove.into_iter().enumerate() {
224 if to_remove {
225 self.stack[i] = None;
226 }
227 }
228
229 if let Some(ref mut heap) = self.heap {
230 heap.retain(f);
231 }
232 }
233
234 fn clear(&mut self) {
235 for el in self.stack.iter_mut() {
236 *el = None;
237 }
238 self.heap = None;
239 }
240}
241
242#[derive(Debug, Clone, Copy, Default)]
243struct PathTimerTable {
244 timers: [Option<Instant>; PathTimer::VALUES.len()],
245}
246
247impl PathTimerTable {
248 fn set(&mut self, timer: PathTimer, time: Instant) {
249 self.timers[timer as usize] = Some(time);
250 }
251
252 fn get(&self, timer: PathTimer) -> Option<Instant> {
253 self.timers[timer as usize]
254 }
255
256 fn stop(&mut self, timer: PathTimer) {
257 self.timers[timer as usize] = None;
258 }
259
260 fn expire_before(&mut self, now: Instant) -> Option<(PathTimer, Instant)> {
262 for timer in PathTimer::VALUES {
263 if self.timers[timer as usize].is_some()
264 && self.timers[timer as usize].expect("checked") <= now
265 {
266 return self.timers[timer as usize].take().map(|time| (timer, time));
267 }
268 }
269
270 None
271 }
272}
273
274impl TimerTable {
275 pub(super) fn set(&mut self, timer: Timer, time: Instant, qlog: QlogSinkWithTime<'_>) {
279 match timer {
280 Timer::Conn(timer) => {
281 self.generic[timer as usize] = Some(time);
282 }
283 Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&PathIdKey(path_id)) {
284 None => {
285 let mut table = PathTimerTable::default();
286 table.set(timer, time);
287 self.path_timers.insert(PathIdKey(path_id), table);
288 }
289 Some(table) => {
290 table.set(timer, time);
291 }
292 },
293 }
294 qlog.emit_timer_set(timer, time);
295 }
296
297 #[allow(unused)]
298 pub(super) fn get(&self, timer: Timer) -> Option<Instant> {
299 match timer {
300 Timer::Conn(timer) => self.generic[timer as usize],
301 Timer::PerPath(path_id, timer) => self.path_timers.get(&PathIdKey(path_id))?.get(timer),
302 }
303 }
304
305 pub(super) fn set_or_stop(
306 &mut self,
307 timer: Timer,
308 time: Option<Instant>,
309 qlog: QlogSinkWithTime<'_>,
310 ) {
311 match time {
312 Some(time) => self.set(timer, time, qlog),
313 None => self.stop(timer, qlog),
314 }
315 }
316
317 pub(super) fn stop(&mut self, timer: Timer, qlog: QlogSinkWithTime<'_>) {
318 match timer {
319 Timer::Conn(timer) => {
320 self.generic[timer as usize] = None;
321 }
322 Timer::PerPath(path_id, timer) => {
323 if let Some(e) = self.path_timers.get_mut(&PathIdKey(path_id)) {
324 e.stop(timer);
325 }
326 }
327 }
328 qlog.emit_timer_stop(timer);
329 }
330
331 pub(super) fn stop_per_path(&mut self, path_id: PathId, qlog: QlogSinkWithTime<'_>) {
333 for timer in PathTimer::VALUES {
334 if let Some(e) = self.path_timers.get_mut(&PathIdKey(path_id)) {
335 e.stop(timer);
336 qlog.emit_timer_stop(Timer::PerPath(path_id, timer));
337 }
338 }
339 }
340
341 pub(super) fn peek(&self) -> Option<Instant> {
343 let min_generic = self.generic.iter().filter_map(|&x| x).min();
346 let min_path = self
347 .path_timers
348 .values()
349 .flat_map(|p| p.timers.iter().filter_map(|&x| x))
350 .min();
351
352 match (min_generic, min_path) {
353 (None, None) => None,
354 (Some(val), None) => Some(val),
355 (Some(a), Some(b)) => Some(a.min(b)),
356 (None, Some(val)) => Some(val),
357 }
358 }
359
360 pub(super) fn expire_before(
362 &mut self,
363 now: Instant,
364 qlog: &QlogSink,
365 ) -> Option<(Timer, Instant)> {
366 let (timer, instant) = self.expire_before_inner(now)?;
367 qlog.with_time(now).emit_timer_expire(timer);
368 Some((timer, instant))
369 }
370
371 fn expire_before_inner(&mut self, now: Instant) -> Option<(Timer, Instant)> {
372 for timer in ConnTimer::VALUES {
375 if self.generic[timer as usize].is_some()
376 && self.generic[timer as usize].expect("checked") <= now
377 {
378 return self.generic[timer as usize]
379 .take()
380 .map(|time| (Timer::Conn(timer), time));
381 }
382 }
383
384 let mut res = None;
385 for (PathIdKey(path_id), timers) in self.path_timers.iter_mut() {
386 if let Some((timer, time)) = timers.expire_before(now) {
387 res = Some((Timer::PerPath(*path_id, timer), time));
388 break;
389 }
390 }
391
392 self.path_timers
394 .retain(|_path_id, timers| timers.timers.iter().any(|t| t.is_some()));
395 res
396 }
397
398 pub(super) fn reset(&mut self) {
399 for timer in ConnTimer::VALUES {
400 self.generic[timer as usize] = None;
401 }
402 self.path_timers.clear();
403 }
404
405 #[cfg(test)]
406 pub(super) fn values(&self) -> Vec<(Timer, Instant)> {
407 let mut values = Vec::new();
408
409 for timer in ConnTimer::VALUES {
410 if let Some(time) = self.generic[timer as usize] {
411 values.push((Timer::Conn(timer), time));
412 }
413 }
414
415 for timer in PathTimer::VALUES {
416 for (PathIdKey(path_id), timers) in self.path_timers.iter() {
417 if let Some(time) = timers.timers[timer as usize] {
418 values.push((Timer::PerPath(*path_id, timer), time));
419 }
420 }
421 }
422
423 values
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use std::time::Duration;
430
431 use crate::connection::qlog::QlogSink;
432
433 use super::*;
434
435 #[test]
436 fn timer_table() {
437 let mut timers = TimerTable::default();
438 let sec = Duration::from_secs(1);
439 let now = Instant::now() + Duration::from_secs(10);
440 timers.set(
441 Timer::Conn(ConnTimer::Idle),
442 now - 3 * sec,
443 QlogSink::default().with_time(now),
444 );
445 timers.set(
446 Timer::Conn(ConnTimer::Close),
447 now - 2 * sec,
448 QlogSink::default().with_time(now),
449 );
450
451 assert_eq!(timers.peek(), Some(now - 3 * sec));
452 assert_eq!(
453 timers.expire_before(now, &QlogSink::default()),
454 Some((Timer::Conn(ConnTimer::Idle), now - 3 * sec))
455 );
456 assert_eq!(
457 timers.expire_before(now, &QlogSink::default()),
458 Some((Timer::Conn(ConnTimer::Close), now - 2 * sec))
459 );
460 assert_eq!(timers.expire_before(now, &QlogSink::default()), None);
461 }
462
463 #[test]
464 fn test_small_map() {
465 let mut map = SmallMap::<usize, usize, 2>::default();
466
467 assert_eq!(map.insert(1, 1), None);
469 assert!(map.heap.is_none());
470 assert_eq!(map.insert(2, 2), None);
471 assert!(map.heap.is_none());
472
473 assert_eq!(map.insert(1, 2), Some(1));
475
476 assert_eq!(map.remove(&1), Some(2));
477 assert_eq!(map.insert(3, 3), None);
478 assert!(map.heap.is_none());
479
480 assert_eq!(map.insert(4, 4), None);
482 assert!(map.heap.is_some());
483
484 assert_eq!(
485 map.iter()
486 .map(|(&a, &b)| (a, b))
487 .collect::<Vec<(usize, usize)>>(),
488 vec![(3, 3), (2, 2), (4, 4)]
489 );
490 assert_eq!(
491 map.iter()
492 .map(|(a, b)| (*a, *b))
493 .collect::<Vec<(usize, usize)>>(),
494 map.iter_mut()
495 .map(|(a, b)| (*a, *b))
496 .collect::<Vec<(usize, usize)>>(),
497 );
498
499 assert_eq!(map.heap.as_ref().unwrap().len(), 1);
500
501 for i in 0..10 {
502 map.insert(10 + i, 10 + i);
503 }
504 assert_eq!(map.heap.as_ref().unwrap().len(), 11);
505 map.retain(|k, _v| *k < 10);
506
507 assert_eq!(map.heap.as_ref().unwrap().len(), 1);
508
509 assert_eq!(
510 map.iter()
511 .map(|(&a, &b)| (a, b))
512 .collect::<Vec<(usize, usize)>>(),
513 vec![(3, 3), (2, 2), (4, 4)]
514 );
515
516 assert_eq!(
517 map.iter()
518 .map(|(a, b)| (*a, *b))
519 .collect::<Vec<(usize, usize)>>(),
520 map.iter_mut()
521 .map(|(a, b)| (*a, *b))
522 .collect::<Vec<(usize, usize)>>(),
523 );
524
525 map.clear();
526 assert_eq!(map.iter().collect::<Vec<_>>(), Vec::new());
527 assert!(map.heap.is_none());
528 }
529}