radiate_core/domain/sync/
control.rs1use std::sync::{
2 Arc, Condvar, Mutex,
3 atomic::{AtomicBool, Ordering},
4};
5
6#[derive(Debug, Default)]
7struct State {
8 paused: bool,
9 permits: usize,
10}
11
12#[derive(Clone, Default)]
13pub struct ThreadSync {
14 stop_flag: Arc<AtomicBool>,
15 inner: Arc<(Mutex<State>, Condvar)>,
16}
17
18impl ThreadSync {
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 pub fn pair() -> (Self, Self) {
33 let ctl = Self::new();
34 (ctl.clone(), ctl)
35 }
36
37 #[inline]
38 pub fn stop(&self) {
39 self.stop_flag.store(true, Ordering::SeqCst);
40 self.set_paused(true);
42 }
43
44 #[inline]
45 pub fn is_stopped(&self) -> bool {
46 self.stop_flag.load(Ordering::Relaxed)
47 }
48
49 #[inline]
50 pub fn stop_flag(&self) -> Arc<AtomicBool> {
51 self.stop_flag.clone()
52 }
53
54 #[inline]
55 pub fn set_paused(&self, paused: bool) {
56 let (lock, cv) = &*self.inner;
57 let mut st = lock.lock().unwrap();
58 st.paused = paused;
59 if !paused {
60 st.permits = 0; }
62 cv.notify_all();
63 }
64
65 #[inline]
66 pub fn toggle_pause(&self) -> bool {
67 let (lock, cv) = &*self.inner;
68 let mut st = lock.lock().unwrap();
69 st.paused = !st.paused;
70 if !st.paused {
71 st.permits = 0;
72 }
73 let now = st.paused;
74 cv.notify_all();
75 now
76 }
77
78 #[inline]
79 pub fn step_once(&self) {
80 self.step_n(1);
81 }
82
83 #[inline]
84 pub fn step_n(&self, n: usize) {
85 let (lock, cv) = &*self.inner;
86 let mut st = lock.lock().unwrap();
87 st.paused = true;
88 st.permits += n;
89 cv.notify_all();
90 }
91
92 #[inline]
93 pub fn wait(&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}
117
118#[cfg(test)]
119mod diag_tests {
120 use super::*;
121 use std::sync::atomic::AtomicUsize;
122 use std::time::Duration;
123
124 #[test]
125 fn step_n_blocks_after_permits_exhausted() {
126 let control = ThreadSync::new();
127 control.step_n(10);
128
129 let count = Arc::new(AtomicUsize::new(0));
130 let count2 = Arc::clone(&count);
131 let control2 = control.clone();
132
133 let handle = std::thread::spawn(move || {
134 for _ in 0..15 {
135 control2.wait();
136 count2.fetch_add(1, Ordering::SeqCst);
137 }
138 });
139
140 std::thread::sleep(Duration::from_millis(300));
141 let progressed = count.load(Ordering::SeqCst);
142 println!("progressed before stop: {progressed}");
143 control.stop();
144 handle.join().unwrap();
145 let after_stop = count.load(Ordering::SeqCst);
146 println!("progressed after stop: {after_stop}");
147
148 assert_eq!(
149 progressed, 10,
150 "expected exactly 10 waits to return before blocking"
151 );
152 }
153}