#![allow(clippy::mutex_atomic)]
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, Sender};
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::Duration;
pub struct Timer {
pub duration: Duration,
shared: Arc<SharedTimerState>,
}
pub struct Stopper {
shared: Arc<SharedTimerState>,
}
struct SharedTimerState {
stopping: AtomicBool,
lock: Mutex<bool>,
signal_tick: Condvar,
tx_stop: Mutex<Sender<()>>,
}
pub trait Stoppable {
fn stop(&self);
}
impl Timer {
#[must_use]
pub fn new<A: AsRef<str>>(dur: Duration, name: A) -> (Self, Stopper) {
let (tx_stop, rx_stop): (Sender<()>, Receiver<()>) = mpsc::channel();
let shared = Arc::new(SharedTimerState {
stopping: AtomicBool::new(false),
lock: Mutex::new(false),
signal_tick: Condvar::new(),
tx_stop: Mutex::new(tx_stop),
});
let shared_c = Arc::clone(&shared);
thread::Builder::new()
.name(format!("timer-{}", name.as_ref()))
.spawn(move || {
loop {
let mut signal = shared_c.lock.lock().unwrap();
*signal = true;
shared_c.signal_tick.notify_one();
drop(signal);
let recv_result = rx_stop.recv_timeout(dur);
if recv_result.is_ok() {
break;
}
}
})
.unwrap();
let shared_c = Arc::clone(&shared);
(
Self {
duration: dur,
shared,
},
Stopper { shared: shared_c },
)
}
}
fn stop_timer(shared: &SharedTimerState) {
shared.stopping.store(true, Ordering::SeqCst);
let mut signal = shared.lock.lock().unwrap();
*signal = true;
drop(signal);
let tx_stop = shared.tx_stop.lock().unwrap();
let _ = tx_stop.send(());
drop(tx_stop);
shared.signal_tick.notify_one();
}
impl Stoppable for Timer {
fn stop(&self) { stop_timer(&self.shared); }
}
impl Stoppable for Stopper {
fn stop(&self) { stop_timer(&self.shared); }
}
impl Drop for Timer {
fn drop(&mut self) {
if !self.shared.stopping.load(Ordering::SeqCst) {
self.stop();
}
}
}
impl Iterator for Timer {
type Item = ();
fn next(&mut self) -> Option<Self::Item> {
let mut next_tick = self.shared.lock.lock().unwrap();
while !*next_tick {
next_tick = self.shared.signal_tick.wait(next_tick).unwrap();
}
*next_tick = false;
if self.shared.stopping.load(Ordering::SeqCst) {
None
} else {
Some(())
}
}
}
impl Clone for Stopper {
fn clone(&self) -> Self {
Self {
shared: Arc::clone(&self.shared),
}
}
}