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> {
77 change_tx: Sender<BoxedProblemChange<S>>,
79 solving: Arc<AtomicBool>,
81 terminate_early: Arc<AtomicBool>,
83}
84
85impl<S: PlanningSolution> SolverHandle<S> {
86 pub fn new() -> (Self, ProblemChangeReceiver<S>) {
90 let (tx, rx) = mpsc::channel();
91 let solving = Arc::new(AtomicBool::new(false));
92 let terminate_early = Arc::new(AtomicBool::new(false));
93
94 let handle = Self {
95 change_tx: tx,
96 solving: Arc::clone(&solving),
97 terminate_early: Arc::clone(&terminate_early),
98 };
99
100 let receiver = ProblemChangeReceiver {
101 change_rx: rx,
102 solving,
103 terminate_early,
104 };
105
106 (handle, receiver)
107 }
108
109 pub fn add_problem_change<P: ProblemChange<S> + 'static>(
114 &self,
115 change: P,
116 ) -> ProblemChangeResult {
117 if !self.solving.load(Ordering::SeqCst) {
118 return ProblemChangeResult::SolverNotRunning;
119 }
120
121 match self.change_tx.send(Box::new(change)) {
122 Ok(()) => ProblemChangeResult::Queued,
123 Err(_) => ProblemChangeResult::QueueFull,
124 }
125 }
126
127 pub fn add_problem_change_boxed(&self, change: BoxedProblemChange<S>) -> ProblemChangeResult {
129 if !self.solving.load(Ordering::SeqCst) {
130 return ProblemChangeResult::SolverNotRunning;
131 }
132
133 match self.change_tx.send(change) {
134 Ok(()) => ProblemChangeResult::Queued,
135 Err(_) => ProblemChangeResult::QueueFull,
136 }
137 }
138
139 pub fn is_solving(&self) -> bool {
141 self.solving.load(Ordering::SeqCst)
142 }
143
144 pub fn terminate_early(&self) {
148 self.terminate_early.store(true, Ordering::SeqCst);
149 }
150
151 pub fn set_solving(&self, solving: bool) {
153 self.solving.store(solving, Ordering::SeqCst);
154 }
155}
156
157impl<S: PlanningSolution> Clone for SolverHandle<S> {
158 fn clone(&self) -> Self {
159 Self {
160 change_tx: self.change_tx.clone(),
161 solving: Arc::clone(&self.solving),
162 terminate_early: Arc::clone(&self.terminate_early),
163 }
164 }
165}
166
167impl<S: PlanningSolution> Debug for SolverHandle<S> {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 f.debug_struct("SolverHandle")
170 .field("solving", &self.solving.load(Ordering::SeqCst))
171 .field(
172 "terminate_early",
173 &self.terminate_early.load(Ordering::SeqCst),
174 )
175 .finish()
176 }
177}
178
179pub struct ProblemChangeReceiver<S: PlanningSolution> {
181 change_rx: Receiver<BoxedProblemChange<S>>,
183 solving: Arc<AtomicBool>,
185 terminate_early: Arc<AtomicBool>,
187}
188
189impl<S: PlanningSolution> ProblemChangeReceiver<S> {
190 pub fn try_recv(&self) -> Option<BoxedProblemChange<S>> {
194 match self.change_rx.try_recv() {
195 Ok(change) => Some(change),
196 Err(TryRecvError::Empty) => None,
197 Err(TryRecvError::Disconnected) => None,
198 }
199 }
200
201 pub fn drain_pending(&self) -> Vec<BoxedProblemChange<S>> {
205 let mut changes = Vec::new();
206 while let Some(change) = self.try_recv() {
207 changes.push(change);
208 }
209 changes
210 }
211
212 pub fn is_terminate_early_requested(&self) -> bool {
214 self.terminate_early.load(Ordering::SeqCst)
215 }
216
217 pub fn set_solving(&self, solving: bool) {
219 self.solving.store(solving, Ordering::SeqCst);
220 }
221
222 pub fn clear_terminate_early(&self) {
224 self.terminate_early.store(false, Ordering::SeqCst);
225 }
226}
227
228impl<S: PlanningSolution> Debug for ProblemChangeReceiver<S> {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 f.debug_struct("ProblemChangeReceiver")
231 .field("solving", &self.solving.load(Ordering::SeqCst))
232 .finish()
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use solverforge_core::score::SimpleScore;
240 use solverforge_scoring::ScoreDirector;
241
242 #[derive(Clone, Debug)]
243 struct TestSolution {
244 value: i32,
245 score: Option<SimpleScore>,
246 }
247
248 impl PlanningSolution for TestSolution {
249 type Score = SimpleScore;
250 fn score(&self) -> Option<Self::Score> {
251 self.score
252 }
253 fn set_score(&mut self, score: Option<Self::Score>) {
254 self.score = score;
255 }
256 }
257
258 #[derive(Debug)]
259 struct IncrementValue;
260
261 impl ProblemChange<TestSolution> for IncrementValue {
262 fn apply(&self, score_director: &mut dyn ScoreDirector<TestSolution>) {
263 score_director.working_solution_mut().value += 1;
264 }
265 }
266
267 #[test]
268 fn handle_creation() {
269 let (handle, _rx) = SolverHandle::<TestSolution>::new();
270 assert!(!handle.is_solving());
271 }
272
273 #[test]
274 fn submit_change_when_solving() {
275 let (handle, rx) = SolverHandle::<TestSolution>::new();
276 handle.set_solving(true);
277
278 let result = handle.add_problem_change(IncrementValue);
279 assert_eq!(result, ProblemChangeResult::Queued);
280
281 let changes = rx.drain_pending();
283 assert_eq!(changes.len(), 1);
284 }
285
286 #[test]
287 fn submit_change_when_not_solving() {
288 let (handle, _rx) = SolverHandle::<TestSolution>::new();
289
290 let result = handle.add_problem_change(IncrementValue);
291 assert_eq!(result, ProblemChangeResult::SolverNotRunning);
292 }
293
294 #[test]
295 fn multiple_changes() {
296 let (handle, rx) = SolverHandle::<TestSolution>::new();
297 handle.set_solving(true);
298
299 handle.add_problem_change(IncrementValue);
300 handle.add_problem_change(IncrementValue);
301 handle.add_problem_change(IncrementValue);
302
303 let changes = rx.drain_pending();
304 assert_eq!(changes.len(), 3);
305 }
306
307 #[test]
308 fn terminate_early() {
309 let (handle, rx) = SolverHandle::<TestSolution>::new();
310
311 assert!(!rx.is_terminate_early_requested());
312 handle.terminate_early();
313 assert!(rx.is_terminate_early_requested());
314
315 rx.clear_terminate_early();
316 assert!(!rx.is_terminate_early_requested());
317 }
318
319 #[test]
320 fn handle_clone() {
321 let (handle1, rx) = SolverHandle::<TestSolution>::new();
322 let handle2 = handle1.clone();
323
324 handle1.set_solving(true);
325 assert!(handle2.is_solving());
326
327 handle2.add_problem_change(IncrementValue);
328 let changes = rx.drain_pending();
329 assert_eq!(changes.len(), 1);
330 }
331}