Skip to main content

radiate_engines/
control.rs

1use std::sync::{
2    Arc, Condvar, Mutex,
3    atomic::{AtomicBool, Ordering},
4};
5
6#[derive(Debug)]
7struct State {
8    paused: bool,
9    permits: usize,
10}
11
12#[derive(Clone)]
13pub struct EngineControl {
14    stop_flag: Arc<AtomicBool>,
15    inner: Arc<(Mutex<State>, Condvar)>,
16}
17
18impl EngineControl {
19    pub fn new() -> Self {
20        Self {
21            stop_flag: Arc::new(AtomicBool::new(false)),
22            inner: Arc::new((
23                Mutex::new(State {
24                    paused: false,
25                    permits: 0,
26                }),
27                Condvar::new(),
28            )),
29        }
30    }
31
32    /// Create two clones for separate threads (convenience).
33    pub fn pair() -> (Self, Self) {
34        let ctl = Self::new();
35        (ctl.clone(), ctl)
36    }
37
38    // ---- stop ----
39    #[inline]
40    pub fn stop(&self) {
41        self.stop_flag.store(true, Ordering::SeqCst);
42        // wake anything blocked
43        self.set_paused(true);
44    }
45
46    #[inline]
47    pub fn is_stopped(&self) -> bool {
48        self.stop_flag.load(Ordering::Relaxed)
49    }
50
51    #[inline]
52    pub fn stop_flag(&self) -> Arc<AtomicBool> {
53        self.stop_flag.clone()
54    }
55
56    // ---- pause/step ----
57    #[inline]
58    pub fn set_paused(&self, paused: bool) {
59        let (lock, cv) = &*self.inner;
60        let mut st = lock.lock().unwrap();
61        st.paused = paused;
62        if !paused {
63            st.permits = 0; // permits irrelevant when running
64        }
65        cv.notify_all();
66    }
67
68    /// Toggle pause. Returns new paused state.
69    #[inline]
70    pub fn toggle_pause(&self) -> bool {
71        let (lock, cv) = &*self.inner;
72        let mut st = lock.lock().unwrap();
73        st.paused = !st.paused;
74        if !st.paused {
75            st.permits = 0;
76        }
77        let now = st.paused;
78        cv.notify_all();
79        now
80    }
81
82    #[inline]
83    pub fn step_once(&self) {
84        let (lock, cv) = &*self.inner;
85        let mut st = lock.lock().unwrap();
86        st.paused = true;
87        st.permits += 1;
88        cv.notify_all();
89    }
90
91    /// Called by engine thread before computing next epoch.
92    #[inline]
93    pub fn wait_before_step(&self) {
94        let (lock, cv) = &*self.inner;
95        let mut st = lock.lock().unwrap();
96
97        while !self.stop_flag.load(Ordering::Relaxed) {
98            if !st.paused {
99                return;
100            }
101
102            if st.permits > 0 {
103                st.permits -= 1;
104                return;
105            }
106
107            st = cv.wait(st).unwrap();
108        }
109    }
110
111    #[inline]
112    pub fn is_paused(&self) -> bool {
113        let (lock, _) = &*self.inner;
114        lock.lock().unwrap().paused
115    }
116}