use std::sync::mpsc::{Sender, Receiver, channel, TryRecvError};
use std::thread;
use std::time::{Instant, Duration};
use std::collections::HashMap;
use std::hash::Hash;
pub struct Timer<T: Send + 'static + Hash + Clone + Copy + Eq > {
sender: Sender<(T, f32)>,
receiver: Receiver<T>,
}
impl<T: Send + 'static + Hash + Clone + Copy + Eq > Timer<T> {
pub fn new(precision: u64) -> Self {
let (sender, thread_receiver) = channel::<(T, f32)>();
let (thread_sender, receiver) = channel();
thread::spawn(move || {
let mut delta;
let mut now = Instant::now();
let mut tasks = HashMap::new();
let mut to_remove = Vec::new();
let sleep_duration = Duration::from_millis(precision);
loop {
delta = Instant::elapsed(&now).as_secs_f32();
now = Instant::now();
loop {
match thread_receiver.try_recv() {
Ok(t) => tasks.insert(t.0, t.1),
Err(err) => match err {
TryRecvError::Empty => break,
TryRecvError::Disconnected => return,
}
};
}
for (id, time) in tasks.iter_mut() {
*time -= delta;
if *time < 0.0 {
if let Err(_) = thread_sender.send(*id) {
return
}
to_remove.push(*id);
}
}
for id in to_remove.drain(..) {
tasks.remove(&id);
}
thread::sleep(sleep_duration);
}
});
Self{ sender, receiver }
}
#[inline]
pub fn countdown(&self, task: T, delay: f32) {
self.sender.send((task, delay)).expect("timer thread is dead, this is probably bug so please \
report it on github");
}
#[inline]
pub fn poll(&self, collector: &mut Vec<T>) {
loop {
match self.receiver.try_recv() {
Ok(t) => collector.push(t),
Err(_) => return,
}
}
}
}
#[cfg(test)]
mod tests {
use super::Timer;
use std::thread;
use std::time::Duration;
#[test]
fn countdown_test() {
let timer = Timer::new(30);
let mut collector = Vec::new();
timer.countdown(1, 0.9);
timer.countdown(2, 1.9);
timer.poll(&mut collector);
assert_eq!(collector, vec![]);
thread::sleep(Duration::from_secs(1));
timer.poll(&mut collector);
assert_eq!(collector, vec![1]);
thread::sleep(Duration::from_secs(1));
timer.poll(&mut collector);
assert_eq!(collector, vec![1, 2]);
}
}