Commit db0921fd authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

Add a timeout estimator to take estimates from another process.

parent 16ec1d21
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ use std::time::Duration;

pub(crate) mod estimator;
pub(crate) mod pareto;
pub(crate) mod readonly;

pub(crate) use estimator::Estimator;

+0 −42
Original line number Diff line number Diff line
@@ -87,45 +87,3 @@ impl Estimator {
        Ok(())
    }
}

/*
/// An enum that can hold an estimator state.
enum EstimatorInner {
    Pareto(ParetoTimeoutEstimator),
}

impl TimeoutEstimatorImpl for EstimatorInner {
    fn note_hop_completed(&mut self, hop: u8, delay: Duration, is_last: bool) {
        match self {
            EstimatorInner::Pareto(mut p) => p.note_hop_completed(hop, delay, is_last)
        }
    }

    fn note_circ_timeout(&mut self, hop: u8, delay: Duration) {
        match self {
            EstimatorInner::Pareto(mut p) => p.note_circ_timeout(hop, delay)
        }
    }

    fn timeouts(&mut self, action: &Action) -> (Duration, Duration) {
        match self {
            EstimatorInner::Pareto(mut p) => p.timeouts(action)
        }
    }

    fn learning_timeouts(&self) -> bool {
        match self {
            EstimatorInner::Pareto(p) => p.learning_timeouts()
        }
    }

    fn update_params(&mut self, params: &tor_netdir::NetParameters) {
        match self {
            EstimatorInner::Pareto(mut p) => p.update_params(params),
        }
    }


}

*/
+8 −0
Original line number Diff line number Diff line
@@ -482,6 +482,14 @@ pub(crate) struct ParetoTimeoutState {
    // XXXX Do we need a HashMap to represent additional fields? I think we may.
}

impl ParetoTimeoutState {
    /// Return the latest base timeout estimate, as recorded in this state.
    pub(crate) fn latest_estimate(&self) -> Option<Duration> {
        self.current_timeout
            .map(|m| Duration::from_millis(m.0.into()))
    }
}

impl ParetoTimeoutEstimator {
    /// Construct a new ParetoTimeoutEstimator from the provided history
    /// object.
+84 −0
Original line number Diff line number Diff line
//! Implement a timeout estimator that just uses another process's estimates.

use crate::timeouts::{pareto::ParetoTimeoutState, Action, TimeoutEstimator};
use std::convert::TryInto;
use std::time::Duration;

/// A timeout estimator based on reading timeouts that another timeout estimator
/// is computing, in another process.
pub(crate) struct ReadonlyTimeoutEstimator {
    /// Are we using the timeouts?
    using_estimates: bool,
    /// Latest estimate from the persistent state.
    latest_timeout: Option<Duration>,
    /// Timeout to use if we don't have a computed timeout.
    default_timeout: Duration,
}

impl ReadonlyTimeoutEstimator {
    /// Create a new ReadonlyTimeoutEstimator with default settings.
    pub(crate) fn new() -> Self {
        ReadonlyTimeoutEstimator {
            using_estimates: true,
            latest_timeout: None,
            default_timeout: Duration::from_secs(60),
        }
    }

    /// Create a new ReadonlyTimeoutEstimator, based on persistent state
    pub(crate) fn from_state(s: &ParetoTimeoutState) -> Self {
        let mut est = Self::new();
        est.update_from_state(s);
        est
    }

    /// Update this estimator based on a newly read state.
    pub(crate) fn update_from_state(&mut self, s: &ParetoTimeoutState) {
        self.latest_timeout = s.latest_estimate();
    }
}

impl TimeoutEstimator for ReadonlyTimeoutEstimator {
    fn note_hop_completed(&mut self, _hop: u8, _delay: Duration, _is_last: bool) {
        // We don't record any timeouts with this estimator.
    }

    fn note_circ_timeout(&mut self, _hop: u8, _delay: Duration) {
        // as above
    }

    fn timeouts(&mut self, action: &Action) -> (Duration, Duration) {
        let base = match (self.using_estimates, self.latest_timeout) {
            (true, Some(d)) => d,
            (_, _) => self.default_timeout,
        };

        let reference_action = Action::BuildCircuit { length: 3 };
        debug_assert!(reference_action.timeout_scale() > 0);

        let multiplier =
            (action.timeout_scale() as f64) / (reference_action.timeout_scale() as f64);

        // XXXX `mul_f64()` can panic if we overflow Duration.
        let timeout = base.mul_f64(multiplier);
        // We use the same timeout twice here, since we don't have separate
        // abandon and timeout thresholds here.
        (timeout, timeout)
    }

    fn learning_timeouts(&self) -> bool {
        false
    }

    fn update_params(&mut self, params: &tor_netdir::params::NetParameters) {
        self.using_estimates = !bool::from(params.cbt_learning_disabled);
        self.default_timeout = params
            .cbt_initial_timeout
            .try_into()
            .unwrap_or_else(|_| Duration::from_secs(60));
    }

    fn build_state(&mut self) -> Option<ParetoTimeoutState> {
        None
    }
}