rustbatch 0.4.0

purely game dewelopment crate that offers simple but powerfull 2D rendering and some fast solutions for game world bottle necks
Documentation
use std::sync::mpsc::{Sender, Receiver, channel, TryRecvError};
use std::thread;
use std::time::{Instant, Duration};
use std::collections::HashMap;
use std::hash::Hash;

/// Timer is for off-thread countdowns. It keeps whatever you input for a duration and then returns
/// it.
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> {
    /// new creates timer thread that will countdown all tasks, precision determinate how frequent
    /// should countdown iterations should be. Passing 0 as precision results to very precise
    /// countdowns but thread will use CPU on 100%. Timer thread of cores terminates when you drop
    /// Timer.
    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 }
    }

    /// countdown starts countdown of task. It can be retrieved by polling after delay. Delay is in
    /// seconds.
    #[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");
    }

    /// poll feeds collector with all finished tasks. Its like this because then you would have to
    /// allocate new vector every poll.
    #[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]);
    }
}