this was more convoluted than I thought. maybe someone will simplify this. Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
125 lines
3.9 KiB
Rust
125 lines
3.9 KiB
Rust
//! Inner Fenrir tracking
|
|
//! This is meant to be **async-free** so that others might use it
|
|
//! without the tokio runtime
|
|
|
|
pub(crate) mod worker;
|
|
|
|
use crate::inner::worker::Work;
|
|
use ::std::{collections::BTreeMap, vec::Vec};
|
|
use ::tokio::time::Instant;
|
|
|
|
/// Track the total number of threads and our index
|
|
/// 65K cpus should be enough for anybody
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub(crate) struct ThreadTracker {
|
|
pub total: u16,
|
|
/// Note: starts from 1
|
|
pub id: u16,
|
|
}
|
|
|
|
pub(crate) static mut SLEEP_RESOLUTION: ::std::time::Duration =
|
|
if cfg!(linux) || cfg!(macos) {
|
|
::std::time::Duration::from_millis(1)
|
|
} else {
|
|
// windows
|
|
::std::time::Duration::from_millis(16)
|
|
};
|
|
|
|
pub(crate) async fn set_minimum_sleep_resolution() {
|
|
let nanosleep = ::std::time::Duration::from_nanos(1);
|
|
let mut tests: usize = 3;
|
|
|
|
while tests > 0 {
|
|
let pre_sleep = ::std::time::Instant::now();
|
|
::tokio::time::sleep(nanosleep).await;
|
|
let post_sleep = ::std::time::Instant::now();
|
|
let slept_for = post_sleep - pre_sleep;
|
|
#[allow(unsafe_code)]
|
|
unsafe {
|
|
if slept_for < SLEEP_RESOLUTION {
|
|
SLEEP_RESOLUTION = slept_for;
|
|
}
|
|
}
|
|
tests = tests - 1;
|
|
}
|
|
}
|
|
|
|
/// Sleeping has a higher resolution that we would like for packet pacing.
|
|
/// So we sleep for however log we need, then chunk up all the work here
|
|
/// we will end up chunking the work in SLEEP_RESOLUTION, then we will busy wait
|
|
/// for more precise timing
|
|
pub(crate) struct Timers {
|
|
times: BTreeMap<Instant, Work>,
|
|
}
|
|
|
|
impl Timers {
|
|
pub(crate) fn new() -> Self {
|
|
Self {
|
|
times: BTreeMap::new(),
|
|
}
|
|
}
|
|
pub(crate) fn get_next(&self) -> ::tokio::time::Sleep {
|
|
match self.times.keys().next() {
|
|
Some(entry) => ::tokio::time::sleep_until((*entry).into()),
|
|
None => {
|
|
::tokio::time::sleep(::std::time::Duration::from_secs(3600))
|
|
}
|
|
}
|
|
}
|
|
pub(crate) fn add(
|
|
&mut self,
|
|
duration: ::tokio::time::Duration,
|
|
work: Work,
|
|
) -> ::tokio::time::Instant {
|
|
// the returned time is the key in the map.
|
|
// Make sure it is unique.
|
|
//
|
|
// We can be pretty sure we won't do a lot of stuff
|
|
// in a single nanosecond, so if we hit a time that is already present
|
|
// just add a nanosecond and retry
|
|
let mut time = ::tokio::time::Instant::now() + duration;
|
|
let mut work = work;
|
|
loop {
|
|
if let Some(old_val) = self.times.insert(time, work) {
|
|
work = self.times.insert(time, old_val).unwrap();
|
|
time = time + ::std::time::Duration::from_nanos(1);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
time
|
|
}
|
|
pub(crate) fn remove(&mut self, time: ::tokio::time::Instant) {
|
|
let _ = self.times.remove(&time);
|
|
}
|
|
/// Get all the work from now up until now + SLEEP_RESOLUTION
|
|
pub(crate) fn get_work(&mut self) -> Vec<Work> {
|
|
let now: ::tokio::time::Instant = ::std::time::Instant::now().into();
|
|
let mut ret = Vec::with_capacity(4);
|
|
let mut count_rm = 0;
|
|
#[allow(unsafe_code)]
|
|
let next_instant = unsafe { now + SLEEP_RESOLUTION };
|
|
let mut iter = self.times.iter_mut().peekable();
|
|
loop {
|
|
match iter.peek() {
|
|
None => break,
|
|
Some(next) => {
|
|
if *next.0 > next_instant {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
let mut work = Work::DropHandshake(crate::enc::asym::KeyID(0));
|
|
let mut entry = iter.next().unwrap();
|
|
::core::mem::swap(&mut work, &mut entry.1);
|
|
ret.push(work);
|
|
count_rm = count_rm + 1;
|
|
}
|
|
while count_rm > 0 {
|
|
self.times.pop_first();
|
|
count_rm = count_rm - 1;
|
|
}
|
|
ret
|
|
}
|
|
}
|