use std::{cmp::Reverse, fmt};
use pi_ext_heap::{empty as heap_empty, ExtHeap};
use pi_wheel::{TimeoutItem, Wheel};
pub struct Timer<T, const N0: usize, const N: usize, const L: usize> {
wheel: Wheel<T, N0, N, L>, heap: ExtHeap<Reverse<TimeoutItem<T>>>, add_count: usize,
remove_count: usize,
roll_count: u64,
}
impl<T: fmt::Debug, const N0: usize, const N: usize, const L: usize> fmt::Debug
for Timer<T, N0, N, L>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Timer")
.field("wheel", &self.wheel)
.field("heap", &self.heap)
.field("add_count", &self.add_count)
.field("remove_count", &self.remove_count)
.field("roll_count", &self.roll_count)
.finish()
}
}
impl<T, const N0: usize, const N: usize, const L: usize> Default for Timer<T, N0, N, L> {
fn default() -> Self {
Timer {
wheel: Default::default(),
heap: Default::default(),
add_count: 0,
remove_count: 0,
roll_count: 0,
}
}
}
impl<T, const N0: usize, const N: usize, const L: usize> Timer<T, N0, N, L> {
pub fn add_count(&self) -> usize {
self.add_count
}
pub fn remove_count(&self) -> usize {
self.remove_count
}
pub fn roll_count(&self) -> u64 {
self.roll_count
}
pub fn push_time(&mut self, time: u64, el: T) {
self.push(match time.checked_sub(self.roll_count) {
Some(r) => r as usize,
_ => 0,
}, el)
}
pub fn push(&mut self, timeout: usize, el: T) {
self.add_count += 1;
if let Some(r) = self.wheel.push(TimeoutItem::new(timeout, el)) {
self.heap.push(Reverse(r), &mut (), heap_empty);
}
}
pub fn pop(&mut self, now: u64) -> Option<T> {
loop {
if let Some(r) = self.wheel.pop() {
self.remove_count += 1;
return Some(r.el)
}
if self.roll_count >= now {
return None
}
self.roll();
}
}
pub fn is_ok(&mut self, now: u64) -> bool {
loop {
if !self.wheel.is_cur_over() {
return true
}
if self.roll_count >= now {
return false
}
self.roll();
}
}
pub fn roll(&mut self) {
self.roll_count += 1;
if self.wheel.roll() {
for i in 0..self.heap.len() {
unsafe { self.heap.get_unchecked_mut(i).0.timeout -= self.wheel.max_time() };
}
while let Some(it) = self.heap.peek() {
if it.0.timeout >= self.wheel.max_time() {
break;
}
let it = self.heap.pop(&mut (), heap_empty).unwrap();
self.wheel.push(it.0);
}
}
}
}
#[cfg(test)]
mod test_mod {
extern crate pcg_rand;
extern crate rand_core;
use std::{
thread,
time::{Duration, Instant},
};
use self::rand_core::{RngCore, SeedableRng};
use crate::*;
#[test]
fn test() {
let mut timer: Timer<(u64, u64), 128, 16, 1> = Default::default();
let mut rng = pcg_rand::Pcg32::seed_from_u64(22222);
let start = Instant::now();
println!("max_time:{}", timer.wheel.max_time());
for i in 1..100000 {
let t = (rng.next_u32() % 16100) as u64;
let now = Instant::now();
let tt = now.duration_since(start).as_millis() as u64;
if i < 100 {
println!("push: timeout:{} realtime:{:?}", t, (i, t + tt));
timer.push(t as usize, (i, t + tt));
}
if t == 9937 || t == 15280 {
println!("{:?}", timer.wheel);
}
while timer.is_ok(tt) {
let it = timer.pop(tt).unwrap();
println!("ppp:{:?}, now:{}", it, tt);
}
if i > 100 && timer.add_count == timer.remove_count {
println!(
"return: add_count:{:?}",
timer.add_count
);
return;
}
thread::sleep(Duration::from_millis(1 as u64));
}
}
}