use std::{
sync::{atomic::Ordering, Arc},
thread::{self, JoinHandle},
time::{Duration, Instant}
};
use atomic_time::AtomicInstant;
use parking_lot::{Condvar, Mutex};
use crate::cmdsig::CmdSignal;
pub(crate) struct Inner {
term: bool
}
pub(crate) struct Shared {
inner: Mutex<Inner>,
signal: Condvar,
dur: Duration,
last_pkt_inst: AtomicInstant,
cmdsig: CmdSignal
}
impl Shared {
pub(crate) fn new(dur: Duration, cmdsig: CmdSignal) -> Self {
let inner = Inner { term: false };
Shared {
inner: Mutex::new(inner),
signal: Condvar::new(),
dur,
last_pkt_inst: AtomicInstant::now(),
cmdsig
}
}
#[inline]
pub(crate) fn touch(&self) {
self.last_pkt_inst.store(Instant::now(), Ordering::Release);
}
pub(crate) fn kill(&self) {
let mut g = self.inner.lock();
g.term = true;
self.signal.notify_one();
}
}
pub(crate) fn run(
idle_dur: Duration,
cmdsig: CmdSignal
) -> (Arc<Shared>, JoinHandle<()>) {
let sh = Shared::new(idle_dur, cmdsig);
let sh = Arc::new(sh);
let sh2 = Arc::clone(&sh);
let jh = thread::spawn(move || idle_thread(sh2));
(sh, jh)
}
pub(crate) fn idle_thread(sh: Arc<Shared>) {
let mut g = sh.inner.lock();
if g.term {
return;
}
let mut last_run = Instant::now();
let mut at = sh.last_pkt_inst.load(Ordering::Acquire) + sh.dur;
loop {
let res = sh.signal.wait_until(&mut g, at);
if g.term {
return;
}
let last_pkt_inst = sh.last_pkt_inst.load(Ordering::Acquire);
if res.timed_out() {
if last_pkt_inst == last_run {
at = Instant::now() + sh.dur;
} else {
sh.cmdsig.idle();
last_run = last_pkt_inst;
at = last_pkt_inst + sh.dur;
}
} else {
at = Instant::now() + sh.dur;
}
}
}