use std::time::Duration;
use std::thread;
use crossbeam_channel::Sender;
use crate::message::Message;
pub struct TimerHandle {
_cancel: Option<std::sync::mpsc::Sender<()>>,
}
pub struct Timer;
impl Timer {
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) }
}
pub fn send_after(tx: Sender<Message>, delay: Duration, msg: Message) -> TimerHandle {
Self::set_timeout(delay, move || { let _ = tx.send(msg); })
}
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);
}
}