use identity_hash::{IdentityHashable, IntMap};
use crate::{
Instant,
connection::qlog::{QlogSink, QlogSinkWithTime},
};
use super::PathId;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub(crate) enum Timer {
Conn(ConnTimer),
PerPath(PathId, PathTimer),
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub(crate) enum ConnTimer {
Idle = 0,
Close = 1,
KeyDiscard = 2,
KeepAlive = 3,
PushNewCid = 4,
NoAvailablePath = 5,
NatTraversalProbeRetry = 6,
}
impl ConnTimer {
const VALUES: [Self; 7] = [
Self::Idle,
Self::Close,
Self::KeyDiscard,
Self::KeepAlive,
Self::PushNewCid,
Self::NoAvailablePath,
Self::NatTraversalProbeRetry,
];
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub(crate) enum PathTimer {
LossDetection = 0,
PathIdle = 1,
PathValidationFailed = 2,
PathChallengeLost = 3,
AbandonFromValidation = 4,
PathKeepAlive = 5,
Pacing = 6,
MaxAckDelay = 7,
PathDrained = 8,
}
impl PathTimer {
pub(super) const VALUES: [Self; 9] = [
Self::LossDetection,
Self::PathIdle,
Self::PathValidationFailed,
Self::PathChallengeLost,
Self::AbandonFromValidation,
Self::PathKeepAlive,
Self::Pacing,
Self::MaxAckDelay,
Self::PathDrained,
];
}
#[derive(Debug, Clone, Default)]
pub(crate) struct TimerTable {
generic: [Option<Instant>; ConnTimer::VALUES.len()],
path_timers: SmallMap<PathId, PathTimerTable, STACK_TIMERS>,
}
const STACK_TIMERS: usize = 4;
#[derive(Debug, Clone)]
struct SmallMap<K, V, const SIZE: usize> {
stack: [Option<(K, V)>; SIZE],
heap: Option<IntMap<K, V>>,
}
impl<K, V, const SIZE: usize> Default for SmallMap<K, V, SIZE> {
fn default() -> Self {
Self {
stack: [const { None }; SIZE],
heap: None,
}
}
}
impl<K, V, const SIZE: usize> SmallMap<K, V, SIZE>
where
K: std::cmp::Eq + std::hash::Hash + IdentityHashable,
{
fn insert(&mut self, key: K, value: V) -> Option<V> {
for el in self.stack.iter_mut() {
match el {
Some((k, v)) => {
if *k == key {
let old_value = std::mem::replace(v, value);
return Some(old_value);
}
}
None => {
let old_heap = self.heap.as_mut().and_then(|h| h.remove(&key));
*el = Some((key, value));
return old_heap;
}
}
}
let heap = self.heap.get_or_insert_default();
heap.insert(key, value)
}
#[cfg(test)]
fn remove(&mut self, key: &K) -> Option<V> {
for el in self.stack.iter_mut() {
if let Some((k, _)) = el
&& key == k
{
return el.take().map(|(_, v)| v);
}
}
self.heap.as_mut().and_then(|h| h.remove(key))
}
fn get(&self, key: &K) -> Option<&V> {
for (k, v) in self.stack.iter().filter_map(|v| v.as_ref()) {
if k == key {
return Some(v);
}
}
self.heap.as_ref().and_then(|h| h.get(key))
}
fn get_mut(&mut self, key: &K) -> Option<&mut V> {
for (k, v) in self.stack.iter_mut().filter_map(|v| v.as_mut()) {
if k == key {
return Some(v);
}
}
self.heap.as_mut().and_then(|h| h.get_mut(key))
}
#[cfg(test)]
fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
let a = self
.stack
.iter()
.filter_map(|v| v.as_ref().map(|(k, v)| (k, v)));
let b = self.heap.iter().flat_map(|h| h.iter());
a.chain(b)
}
fn values(&self) -> impl Iterator<Item = &V> {
let a = self.stack.iter().filter_map(|v| v.as_ref().map(|(_, v)| v));
let b = self.heap.iter().flat_map(|h| h.values());
a.chain(b)
}
fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
let a = self
.stack
.iter_mut()
.filter_map(|v| v.as_mut().map(|(k, v)| (&*k, v)));
let b = self.heap.iter_mut().flat_map(|h| h.iter_mut());
a.chain(b)
}
fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&K, &mut V) -> bool,
{
let mut to_remove = [false; SIZE];
for (i, el) in self.stack.iter_mut().enumerate() {
if let Some((key, value)) = el {
to_remove[i] = !f(key, value);
}
}
for (i, to_remove) in to_remove.into_iter().enumerate() {
if to_remove {
self.stack[i] = None;
}
}
if let Some(ref mut heap) = self.heap {
heap.retain(f);
}
}
fn clear(&mut self) {
for el in self.stack.iter_mut() {
*el = None;
}
self.heap = None;
}
}
#[derive(Debug, Clone, Copy, Default)]
struct PathTimerTable {
timers: [Option<Instant>; PathTimer::VALUES.len()],
}
impl PathTimerTable {
fn set(&mut self, timer: PathTimer, time: Instant) {
self.timers[timer as usize] = Some(time);
}
fn get(&self, timer: PathTimer) -> Option<Instant> {
self.timers[timer as usize]
}
fn stop(&mut self, timer: PathTimer) {
self.timers[timer as usize] = None;
}
fn expire_before(&mut self, now: Instant) -> Option<(PathTimer, Instant)> {
for timer in PathTimer::VALUES {
if self.timers[timer as usize].is_some()
&& self.timers[timer as usize].expect("checked") <= now
{
return self.timers[timer as usize].take().map(|time| (timer, time));
}
}
None
}
}
impl TimerTable {
pub(super) fn set(&mut self, timer: Timer, time: Instant, qlog: QlogSinkWithTime<'_>) {
match timer {
Timer::Conn(timer) => {
self.generic[timer as usize] = Some(time);
}
Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&path_id) {
None => {
let mut table = PathTimerTable::default();
table.set(timer, time);
self.path_timers.insert(path_id, table);
}
Some(table) => {
table.set(timer, time);
}
},
}
qlog.emit_timer_set(timer, time);
}
pub(super) fn get(&self, timer: Timer) -> Option<Instant> {
match timer {
Timer::Conn(timer) => self.generic[timer as usize],
Timer::PerPath(path_id, timer) => self.path_timers.get(&path_id)?.get(timer),
}
}
pub(super) fn set_or_stop(
&mut self,
timer: Timer,
time: Option<Instant>,
qlog: QlogSinkWithTime<'_>,
) {
match time {
Some(time) => self.set(timer, time, qlog),
None => self.stop(timer, qlog),
}
}
pub(super) fn stop(&mut self, timer: Timer, qlog: QlogSinkWithTime<'_>) {
match timer {
Timer::Conn(timer) => {
self.generic[timer as usize] = None;
}
Timer::PerPath(path_id, timer) => {
if let Some(e) = self.path_timers.get_mut(&path_id) {
e.stop(timer);
}
}
}
qlog.emit_timer_stop(timer);
}
pub(super) fn stop_per_path(&mut self, path_id: PathId, qlog: QlogSinkWithTime<'_>) {
for timer in PathTimer::VALUES {
if let Some(e) = self.path_timers.get_mut(&path_id) {
e.stop(timer);
qlog.emit_timer_stop(Timer::PerPath(path_id, timer));
}
}
}
pub(super) fn peek(&mut self) -> Option<Instant> {
let min_generic = self.generic.iter().filter_map(|&x| x).min();
let min_path = self
.path_timers
.values()
.flat_map(|p| p.timers.iter().filter_map(|&x| x))
.min();
match (min_generic, min_path) {
(None, None) => None,
(Some(val), None) => Some(val),
(Some(a), Some(b)) => Some(a.min(b)),
(None, Some(val)) => Some(val),
}
}
pub(super) fn expire_before(
&mut self,
now: Instant,
qlog: &QlogSink,
) -> Option<(Timer, Instant)> {
let (timer, instant) = self.expire_before_inner(now)?;
qlog.with_time(now).emit_timer_expire(timer);
Some((timer, instant))
}
fn expire_before_inner(&mut self, now: Instant) -> Option<(Timer, Instant)> {
for timer in ConnTimer::VALUES {
if self.generic[timer as usize].is_some()
&& self.generic[timer as usize].expect("checked") <= now
{
return self.generic[timer as usize]
.take()
.map(|time| (Timer::Conn(timer), time));
}
}
let mut res = None;
for (path_id, timers) in self.path_timers.iter_mut() {
if let Some((timer, time)) = timers.expire_before(now) {
res = Some((Timer::PerPath(*path_id, timer), time));
break;
}
}
self.path_timers
.retain(|_path_id, timers| timers.timers.iter().any(|t| t.is_some()));
res
}
pub(super) fn reset(&mut self) {
for timer in ConnTimer::VALUES {
self.generic[timer as usize] = None;
}
self.path_timers.clear();
}
#[cfg(test)]
pub(super) fn values(&self) -> Vec<(Timer, Instant)> {
let mut values = Vec::new();
for timer in ConnTimer::VALUES {
if let Some(time) = self.generic[timer as usize] {
values.push((Timer::Conn(timer), time));
}
}
for timer in PathTimer::VALUES {
for (path_id, timers) in self.path_timers.iter() {
if let Some(time) = timers.timers[timer as usize] {
values.push((Timer::PerPath(*path_id, timer), time));
}
}
}
values
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::connection::qlog::QlogSink;
use super::*;
#[test]
fn timer_table() {
let mut timers = TimerTable::default();
let sec = Duration::from_secs(1);
let now = Instant::now() + Duration::from_secs(10);
timers.set(
Timer::Conn(ConnTimer::Idle),
now - 3 * sec,
QlogSink::default().with_time(now),
);
timers.set(
Timer::Conn(ConnTimer::Close),
now - 2 * sec,
QlogSink::default().with_time(now),
);
assert_eq!(timers.peek(), Some(now - 3 * sec));
assert_eq!(
timers.expire_before(now, &QlogSink::default()),
Some((Timer::Conn(ConnTimer::Idle), now - 3 * sec))
);
assert_eq!(
timers.expire_before(now, &QlogSink::default()),
Some((Timer::Conn(ConnTimer::Close), now - 2 * sec))
);
assert_eq!(timers.expire_before(now, &QlogSink::default()), None);
}
#[test]
fn test_small_map() {
let mut map = SmallMap::<usize, usize, 2>::default();
assert_eq!(map.insert(1, 1), None);
assert!(map.heap.is_none());
assert_eq!(map.insert(2, 2), None);
assert!(map.heap.is_none());
assert_eq!(map.insert(1, 2), Some(1));
assert_eq!(map.remove(&1), Some(2));
assert_eq!(map.insert(3, 3), None);
assert!(map.heap.is_none());
assert_eq!(map.insert(4, 4), None);
assert!(map.heap.is_some());
assert_eq!(
map.iter()
.map(|(&a, &b)| (a, b))
.collect::<Vec<(usize, usize)>>(),
vec![(3, 3), (2, 2), (4, 4)]
);
assert_eq!(
map.iter()
.map(|(a, b)| (*a, *b))
.collect::<Vec<(usize, usize)>>(),
map.iter_mut()
.map(|(a, b)| (*a, *b))
.collect::<Vec<(usize, usize)>>(),
);
assert_eq!(map.heap.as_ref().unwrap().len(), 1);
for i in 0..10 {
map.insert(10 + i, 10 + i);
}
assert_eq!(map.heap.as_ref().unwrap().len(), 11);
map.retain(|k, _v| *k < 10);
assert_eq!(map.heap.as_ref().unwrap().len(), 1);
assert_eq!(
map.iter()
.map(|(&a, &b)| (a, b))
.collect::<Vec<(usize, usize)>>(),
vec![(3, 3), (2, 2), (4, 4)]
);
assert_eq!(
map.iter()
.map(|(a, b)| (*a, *b))
.collect::<Vec<(usize, usize)>>(),
map.iter_mut()
.map(|(a, b)| (*a, *b))
.collect::<Vec<(usize, usize)>>(),
);
map.clear();
assert_eq!(map.iter().collect::<Vec<_>>(), Vec::new());
assert!(map.heap.is_none());
}
}