use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Condvar, Mutex};
use crate::{Error, Result};
#[derive(Default)]
struct State {
paused: AtomicBool,
cancelled: AtomicBool,
lock: Mutex<()>,
wake: Condvar,
}
#[derive(Clone, Default)]
pub struct RunControl {
state: Arc<State>,
}
impl RunControl {
pub fn pause(&self) {
self.state.paused.store(true, Ordering::Release);
}
pub fn resume(&self) {
self.state.paused.store(false, Ordering::Release);
self.state.wake.notify_all();
}
pub fn cancel(&self) {
self.state.cancelled.store(true, Ordering::Release);
self.state.wake.notify_all();
}
#[must_use]
pub fn is_paused(&self) -> bool {
self.state.paused.load(Ordering::Acquire)
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.state.cancelled.load(Ordering::Acquire)
}
pub fn checkpoint(&self) -> Result<()> {
if self.is_cancelled() {
return Err(Error::Cancelled);
}
if self.is_paused() {
let mut guard = self
.state
.lock
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
while self.is_paused() && !self.is_cancelled() {
guard = self
.state
.wake
.wait(guard)
.unwrap_or_else(std::sync::PoisonError::into_inner);
}
}
if self.is_cancelled() {
Err(Error::Cancelled)
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::RunControl;
#[test]
fn pause_resume_and_cancel() {
let control = RunControl::default();
control.pause();
assert!(control.is_paused());
control.resume();
assert!(control.checkpoint().is_ok());
control.cancel();
assert!(control.checkpoint().is_err());
}
}