1use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::{Arc, Condvar, Mutex};
5
6use crate::{Error, Result};
7
8#[derive(Default)]
9struct State {
10 paused: AtomicBool,
11 cancelled: AtomicBool,
12 lock: Mutex<()>,
13 wake: Condvar,
14}
15
16#[derive(Clone, Default)]
18pub struct RunControl {
19 state: Arc<State>,
20}
21
22impl RunControl {
23 pub fn pause(&self) {
25 self.state.paused.store(true, Ordering::Release);
26 }
27
28 pub fn resume(&self) {
30 self.state.paused.store(false, Ordering::Release);
31 self.state.wake.notify_all();
32 }
33
34 pub fn cancel(&self) {
36 self.state.cancelled.store(true, Ordering::Release);
37 self.state.wake.notify_all();
38 }
39
40 #[must_use]
42 pub fn is_paused(&self) -> bool {
43 self.state.paused.load(Ordering::Acquire)
44 }
45
46 #[must_use]
48 pub fn is_cancelled(&self) -> bool {
49 self.state.cancelled.load(Ordering::Acquire)
50 }
51
52 pub fn checkpoint(&self) -> Result<()> {
58 if self.is_cancelled() {
59 return Err(Error::Cancelled);
60 }
61 if self.is_paused() {
62 let mut guard = self
63 .state
64 .lock
65 .lock()
66 .unwrap_or_else(std::sync::PoisonError::into_inner);
67 while self.is_paused() && !self.is_cancelled() {
68 guard = self
69 .state
70 .wake
71 .wait(guard)
72 .unwrap_or_else(std::sync::PoisonError::into_inner);
73 }
74 }
75 if self.is_cancelled() {
76 Err(Error::Cancelled)
77 } else {
78 Ok(())
79 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::RunControl;
86
87 #[test]
88 fn pause_resume_and_cancel() {
89 let control = RunControl::default();
90 control.pause();
91 assert!(control.is_paused());
92 control.resume();
93 assert!(control.checkpoint().is_ok());
94 control.cancel();
95 assert!(control.checkpoint().is_err());
96 }
97}