use {
crossbeam::channel::{
Receiver,
Sender,
},
std::{
thread,
time::Duration,
},
};
pub type TickBeamId = usize;
struct TickBeamHandle {
id: TickBeamId,
interrupt_sender: Sender<()>,
}
pub struct TickBeam<P> {
remaining_count: Option<usize>,
period: Duration,
payload: P,
}
pub struct Ticker<P> {
next_id: usize,
beams: Vec<TickBeamHandle>,
tick_sender: Sender<P>,
pub tick_receiver: Receiver<P>,
}
impl TickBeamHandle {
pub fn stop(self) {
let _ = self.interrupt_sender.send(());
}
}
impl<P> Ticker<P> {
pub fn new() -> Self {
let (tick_sender, tick_receiver) = crossbeam::channel::unbounded();
Self {
next_id: 0,
beams: Vec::new(),
tick_sender,
tick_receiver,
}
}
pub fn stop_all_beams(&mut self) {
for beam in self.beams.drain(..) {
beam.stop();
}
}
pub fn stop_beam(&mut self, id: TickBeamId) {
let idx = self.beams.iter().position(|beam| beam.id == id);
if let Some(idx) = idx {
self.beams.swap_remove(idx).stop();
}
}
}
impl<P: Copy + Send + 'static> Ticker<P> {
pub fn start_beam(&mut self, mission: TickBeam<P>) -> TickBeamId {
let id = self.next_id;
self.next_id += 1;
let (interrupt_sender, interrupt_receiver) = crossbeam::channel::bounded(1);
let tick_sender = self.tick_sender.clone();
thread::spawn(move || {
let mut remaining_count = mission.remaining_count;
loop {
if let Some(remaining_count) = remaining_count.as_mut() {
if *remaining_count == 0 {
break;
}
*remaining_count -= 1;
}
if interrupt_receiver.recv_timeout(mission.period).is_ok() {
break;
}
let _ = tick_sender.send(mission.payload);
}
});
self.beams.push(TickBeamHandle {
id,
interrupt_sender,
});
id
}
pub fn tick_once(&mut self, payload: P, after: Duration) -> TickBeamId {
self.start_beam(TickBeam {
remaining_count: Some(1),
period: after,
payload,
})
}
pub fn tick_several_times(&mut self, payload: P, period: Duration, count: usize) -> TickBeamId {
self.start_beam(TickBeam {
remaining_count: Some(count),
period,
payload,
})
}
pub fn tick_infinitely(&mut self, payload: P, period: Duration) -> TickBeamId {
self.start_beam(TickBeam {
remaining_count: None,
period,
payload,
})
}
}
impl<P> Drop for Ticker<P> {
fn drop(&mut self) {
self.stop_all_beams();
}
}