use std::{
sync::{
Arc,
Mutex,
Condvar,
LockResult,
},
thread::JoinHandle,
time::Duration,
};
use super::{
mutex,
ProglessInner,
};
#[cfg(any(feature = "signals_sigint", feature = "signals_sigwinch"))]
use super::signals::ProglessSignals;
#[derive(Debug)]
pub(super) struct ProglessSteady {
ticker: Mutex<Option<JoinHandle<()>>>,
state: Arc<(Mutex<bool>, Condvar)>,
}
impl Default for ProglessSteady {
#[inline]
fn default() -> Self {
Self {
ticker: Mutex::new(None),
state: Arc::new((Mutex::new(true), Condvar::new())),
}
}
}
impl From<Arc<ProglessInner>> for ProglessSteady {
#[inline]
fn from(t_inner: Arc<ProglessInner>) -> Self {
let state = Arc::new((Mutex::new(false), Condvar::new()));
let t_state = Arc::clone(&state);
Self {
state,
ticker: Mutex::new(Some(spawn_ticker(t_state, t_inner))),
}
}
}
impl ProglessSteady {
const TICK_RATE: Duration = Duration::from_millis(100);
pub(super) fn start(&self, t_inner: Arc<ProglessInner>) {
self.stop();
*mutex!(self.state.0) = false;
let t_state = Arc::clone(&self.state);
mutex!(self.ticker).replace(spawn_ticker(t_state, t_inner));
}
#[inline]
pub(super) fn stop(&self) {
let handle = mutex!(self.ticker).take();
if let Some(handle) = handle {
if ! *mutex!(self.state.0) {
*mutex!(self.state.0) = true;
self.state.1.notify_all();
}
handle.join().unwrap();
}
}
}
impl Drop for ProglessSteady {
#[inline]
fn drop(&mut self) { self.stop(); }
}
#[inline]
fn spawn_ticker(t_state: Arc<(Mutex<bool>, Condvar)>, t_inner: Arc<ProglessInner>)
-> JoinHandle<()> {
std::thread::spawn(move || {
#[cfg(any(feature = "signals_sigint", feature = "signals_sigwinch"))]
let signals = ProglessSignals::default();
let (t_dead, t_cond) = &*t_state;
let mut state = mutex!(t_dead);
while let LockResult::Ok(res) = t_cond.wait_timeout(state, ProglessSteady::TICK_RATE) {
state = res.0;
if *state { return; }
cfg_select! {
any(feature = "signals_sigint", feature = "signals_sigwinch") =>
if ! signals.pretick(&t_inner) || ! t_inner.tick(false) {
*state = true; drop(state);
drop(signals);
return;
},
_ => if ! t_inner.tick(false) {
*state = true; drop(state);
return;
},
}
}
})
}