solverforge_solver/realtime/
solver_handle.rs1use std::fmt::Debug;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
6use std::sync::Arc;
7
8use solverforge_core::domain::PlanningSolution;
9
10use super::problem_change::BoxedProblemChange;
11use super::ProblemChange;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum ProblemChangeResult {
16 Queued,
18 SolverNotRunning,
20 QueueFull,
22}
23
24pub struct SolverHandle<S: PlanningSolution> {
76 change_tx: Sender<BoxedProblemChange<S>>,
78 solving: Arc<AtomicBool>,
80 terminate_early: Arc<AtomicBool>,
82}
83
84impl<S: PlanningSolution> SolverHandle<S> {
85 pub fn new() -> (Self, ProblemChangeReceiver<S>) {
89 let (tx, rx) = mpsc::channel();
90 let solving = Arc::new(AtomicBool::new(false));
91 let terminate_early = Arc::new(AtomicBool::new(false));
92
93 let handle = Self {
94 change_tx: tx,
95 solving: Arc::clone(&solving),
96 terminate_early: Arc::clone(&terminate_early),
97 };
98
99 let receiver = ProblemChangeReceiver {
100 change_rx: rx,
101 solving,
102 terminate_early,
103 };
104
105 (handle, receiver)
106 }
107
108 pub fn add_problem_change<P: ProblemChange<S> + 'static>(
112 &self,
113 change: P,
114 ) -> ProblemChangeResult {
115 if !self.solving.load(Ordering::SeqCst) {
116 return ProblemChangeResult::SolverNotRunning;
117 }
118
119 match self.change_tx.send(Box::new(change)) {
120 Ok(()) => ProblemChangeResult::Queued,
121 Err(_) => ProblemChangeResult::QueueFull,
122 }
123 }
124
125 pub fn add_problem_change_boxed(&self, change: BoxedProblemChange<S>) -> ProblemChangeResult {
127 if !self.solving.load(Ordering::SeqCst) {
128 return ProblemChangeResult::SolverNotRunning;
129 }
130
131 match self.change_tx.send(change) {
132 Ok(()) => ProblemChangeResult::Queued,
133 Err(_) => ProblemChangeResult::QueueFull,
134 }
135 }
136
137 pub fn is_solving(&self) -> bool {
138 self.solving.load(Ordering::SeqCst)
139 }
140
141 pub fn terminate_early(&self) {
145 self.terminate_early.store(true, Ordering::SeqCst);
146 }
147
148 pub fn set_solving(&self, solving: bool) {
149 self.solving.store(solving, Ordering::SeqCst);
150 }
151}
152
153impl<S: PlanningSolution> Clone for SolverHandle<S> {
154 fn clone(&self) -> Self {
155 Self {
156 change_tx: self.change_tx.clone(),
157 solving: Arc::clone(&self.solving),
158 terminate_early: Arc::clone(&self.terminate_early),
159 }
160 }
161}
162
163impl<S: PlanningSolution> Debug for SolverHandle<S> {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 f.debug_struct("SolverHandle")
166 .field("solving", &self.solving.load(Ordering::SeqCst))
167 .field(
168 "terminate_early",
169 &self.terminate_early.load(Ordering::SeqCst),
170 )
171 .finish()
172 }
173}
174
175pub struct ProblemChangeReceiver<S: PlanningSolution> {
177 change_rx: Receiver<BoxedProblemChange<S>>,
179 solving: Arc<AtomicBool>,
181 terminate_early: Arc<AtomicBool>,
183}
184
185impl<S: PlanningSolution> ProblemChangeReceiver<S> {
186 pub fn try_recv(&self) -> Option<BoxedProblemChange<S>> {
190 match self.change_rx.try_recv() {
191 Ok(change) => Some(change),
192 Err(TryRecvError::Empty) => None,
193 Err(TryRecvError::Disconnected) => None,
194 }
195 }
196
197 pub fn drain_pending(&self) -> Vec<BoxedProblemChange<S>> {
200 let mut changes = Vec::new();
201 while let Some(change) = self.try_recv() {
202 changes.push(change);
203 }
204 changes
205 }
206
207 pub fn is_terminate_early_requested(&self) -> bool {
208 self.terminate_early.load(Ordering::SeqCst)
209 }
210
211 pub fn set_solving(&self, solving: bool) {
212 self.solving.store(solving, Ordering::SeqCst);
213 }
214
215 pub fn clear_terminate_early(&self) {
217 self.terminate_early.store(false, Ordering::SeqCst);
218 }
219}
220
221impl<S: PlanningSolution> Debug for ProblemChangeReceiver<S> {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 f.debug_struct("ProblemChangeReceiver")
224 .field("solving", &self.solving.load(Ordering::SeqCst))
225 .finish()
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use solverforge_core::score::SoftScore;
233 use solverforge_scoring::Director;
234
235 #[derive(Clone, Debug)]
236 struct TestSolution {
237 value: i32,
238 score: Option<SoftScore>,
239 }
240
241 impl PlanningSolution for TestSolution {
242 type Score = SoftScore;
243 fn score(&self) -> Option<Self::Score> {
244 self.score
245 }
246 fn set_score(&mut self, score: Option<Self::Score>) {
247 self.score = score;
248 }
249 }
250
251 #[derive(Debug)]
252 struct IncrementValue;
253
254 impl ProblemChange<TestSolution> for IncrementValue {
255 fn apply(&self, score_director: &mut dyn Director<TestSolution>) {
256 score_director.working_solution_mut().value += 1;
257 }
258 }
259
260 #[test]
261 fn handle_creation() {
262 let (handle, _rx) = SolverHandle::<TestSolution>::new();
263 assert!(!handle.is_solving());
264 }
265
266 #[test]
267 fn submit_change_when_solving() {
268 let (handle, rx) = SolverHandle::<TestSolution>::new();
269 handle.set_solving(true);
270
271 let result = handle.add_problem_change(IncrementValue);
272 assert_eq!(result, ProblemChangeResult::Queued);
273
274 let changes = rx.drain_pending();
276 assert_eq!(changes.len(), 1);
277 }
278
279 #[test]
280 fn submit_change_when_not_solving() {
281 let (handle, _rx) = SolverHandle::<TestSolution>::new();
282
283 let result = handle.add_problem_change(IncrementValue);
284 assert_eq!(result, ProblemChangeResult::SolverNotRunning);
285 }
286
287 #[test]
288 fn multiple_changes() {
289 let (handle, rx) = SolverHandle::<TestSolution>::new();
290 handle.set_solving(true);
291
292 handle.add_problem_change(IncrementValue);
293 handle.add_problem_change(IncrementValue);
294 handle.add_problem_change(IncrementValue);
295
296 let changes = rx.drain_pending();
297 assert_eq!(changes.len(), 3);
298 }
299
300 #[test]
301 fn terminate_early() {
302 let (handle, rx) = SolverHandle::<TestSolution>::new();
303
304 assert!(!rx.is_terminate_early_requested());
305 handle.terminate_early();
306 assert!(rx.is_terminate_early_requested());
307
308 rx.clear_terminate_early();
309 assert!(!rx.is_terminate_early_requested());
310 }
311
312 #[test]
313 fn handle_clone() {
314 let (handle1, rx) = SolverHandle::<TestSolution>::new();
315 let handle2 = handle1.clone();
316
317 handle1.set_solving(true);
318 assert!(handle2.is_solving());
319
320 handle2.add_problem_change(IncrementValue);
321 let changes = rx.drain_pending();
322 assert_eq!(changes.len(), 1);
323 }
324}