solverforge-solver 0.12.0

Solver engine for SolverForge
Documentation
use std::time::{Duration, Instant};

use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use tokio::sync::mpsc::{error::TryRecvError, UnboundedReceiver};

use super::super::SolverEvent;
use crate::phase::Phase;
use crate::scope::SolverScope;

const EVENT_TIMEOUT: Duration = Duration::from_secs(30);

pub(super) fn recv_event<S>(
    receiver: &mut UnboundedReceiver<SolverEvent<S>>,
    context: &str,
) -> SolverEvent<S>
where
    S: PlanningSolution,
{
    let started_at = Instant::now();
    loop {
        match receiver.try_recv() {
            Ok(event) => return event,
            Err(TryRecvError::Empty) if started_at.elapsed() < EVENT_TIMEOUT => {
                std::thread::yield_now();
            }
            Err(TryRecvError::Empty) => {
                panic!("timed out after {EVENT_TIMEOUT:?} waiting for {context}");
            }
            Err(TryRecvError::Disconnected) => {
                panic!("event stream disconnected while waiting for {context}");
            }
        }
    }
}

#[derive(Debug, Clone)]
pub(super) struct NoOpPhase;

impl<S, D, ProgressCb> Phase<S, D, ProgressCb> for NoOpPhase
where
    S: PlanningSolution,
    D: Director<S>,
    ProgressCb: crate::scope::ProgressCallback<S>,
{
    fn solve(&mut self, solver_scope: &mut SolverScope<S, D, ProgressCb>) {
        solver_scope.update_best_solution();
    }

    fn phase_type_name(&self) -> &'static str {
        "NoOpPhase"
    }
}