use std::sync::{
Arc, Condvar, Mutex,
atomic::{AtomicBool, Ordering},
};
#[derive(Debug, Default)]
struct State {
paused: bool,
permits: usize,
}
#[derive(Clone, Default)]
pub struct EngineControl {
stop_flag: Arc<AtomicBool>,
inner: Arc<(Mutex<State>, Condvar)>,
}
impl EngineControl {
pub fn new() -> Self {
Self {
stop_flag: Arc::new(AtomicBool::new(false)),
inner: Arc::new((
Mutex::new(State {
paused: false,
permits: 0,
}),
Condvar::new(),
)),
}
}
pub fn pair() -> (Self, Self) {
let ctl = Self::new();
(ctl.clone(), ctl)
}
#[inline]
pub fn stop(&self) {
self.stop_flag.store(true, Ordering::SeqCst);
self.set_paused(true);
}
#[inline]
pub fn is_stopped(&self) -> bool {
self.stop_flag.load(Ordering::Relaxed)
}
#[inline]
pub fn stop_flag(&self) -> Arc<AtomicBool> {
self.stop_flag.clone()
}
#[inline]
pub fn set_paused(&self, paused: bool) {
let (lock, cv) = &*self.inner;
let mut st = lock.lock().unwrap();
st.paused = paused;
if !paused {
st.permits = 0; }
cv.notify_all();
}
#[inline]
pub fn toggle_pause(&self) -> bool {
let (lock, cv) = &*self.inner;
let mut st = lock.lock().unwrap();
st.paused = !st.paused;
if !st.paused {
st.permits = 0;
}
let now = st.paused;
cv.notify_all();
now
}
#[inline]
pub fn step_once(&self) {
let (lock, cv) = &*self.inner;
let mut st = lock.lock().unwrap();
st.paused = true;
st.permits += 1;
cv.notify_all();
}
#[inline]
pub fn wait_before_step(&self) {
let (lock, cv) = &*self.inner;
let mut st = lock.lock().unwrap();
while !self.stop_flag.load(Ordering::Relaxed) {
if !st.paused {
return;
}
if st.permits > 0 {
st.permits -= 1;
return;
}
st = cv.wait(st).unwrap();
}
}
#[inline]
pub fn is_paused(&self) -> bool {
let (lock, _) = &*self.inner;
lock.lock().unwrap().paused
}
}