sprite-core 0.1.7

Sprite Engine — a fault-tolerant actor runtime for Rust
Documentation
use std::time::Duration;
use std::thread;
use crossbeam_channel::Sender;
use crate::message::Message;

/// A handle to a scheduled timer. Drop it to cancel (best-effort).
pub struct TimerHandle {
    _cancel: Option<std::sync::mpsc::Sender<()>>,
}

/// Schedule delayed and repeating messages.
pub struct Timer;

impl Timer {
    /// Run a closure after a delay.
    pub fn set_timeout<F>(delay: Duration, f: F) -> TimerHandle
    where F: FnOnce() + Send + 'static,
    {
        let (cancel_tx, cancel_rx) = std::sync::mpsc::channel();
        thread::spawn(move || {
            match cancel_rx.recv_timeout(delay) {
                Ok(()) => return,
                Err(_) => f(),
            }
        });
        TimerHandle { _cancel: Some(cancel_tx) }
    }

    /// Send a message after a delay.
    pub fn send_after(tx: Sender<Message>, delay: Duration, msg: Message) -> TimerHandle {
        Self::set_timeout(delay, move || { let _ = tx.send(msg); })
    }

    /// Repeatedly send a message every interval.
    pub fn set_interval(tx: Sender<Message>, interval: Duration, msg: Message) -> TimerHandle {
        let (cancel_tx, cancel_rx) = std::sync::mpsc::channel();
        thread::spawn(move || {
            loop {
                match cancel_rx.recv_timeout(interval) {
                    Ok(()) => break,
                    Err(_) => { if tx.send(msg.clone()).is_err() { break; } }
                }
            }
        });
        TimerHandle { _cancel: Some(cancel_tx) }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crossbeam_channel::unbounded;
    #[test]
    fn timeout_fires() {
        let (tx, rx) = unbounded();
        let _ = Timer::send_after(tx, Duration::from_millis(10), Message::text("hi"));
        let msg = rx.recv_timeout(Duration::from_millis(100)).unwrap();
        assert_eq!(msg.as_str(), Some("hi"));
    }
    #[test]
    fn interval_fires() {
        let (tx, rx) = unbounded();
        let _ = Timer::set_interval(tx, Duration::from_millis(5), Message::text("tick"));
        let mut count = 0;
        while rx.recv_timeout(Duration::from_millis(50)).is_ok() {
            count += 1;
            if count >= 3 { break; }
        }
        assert!(count >= 3);
    }
}