1
//! Implement a timeout estimator that just uses another process's estimates.
2

            
3
use crate::timeouts::{pareto::ParetoTimeoutState, Action, TimeoutEstimator};
4
use std::time::Duration;
5

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

            
17
impl ReadonlyTimeoutEstimator {
18
    /// Create a new ReadonlyTimeoutEstimator with default settings.
19
140
    pub(crate) fn new() -> Self {
20
140
        ReadonlyTimeoutEstimator {
21
140
            using_estimates: true,
22
140
            latest_timeout: None,
23
140
            default_timeout: Duration::from_secs(60),
24
140
        }
25
140
    }
26

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

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

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

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

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

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

            
58
6
        let multiplier =
59
6
            (action.timeout_scale() as f64) / (reference_action.timeout_scale() as f64);
60

            
61
        use super::mul_duration_f64_saturating as mul;
62
6
        let timeout = mul(base, multiplier);
63
6

            
64
6
        // We use the same timeout twice here, since we don't need
65
6
        // separate abandon and timeout thresholds when we are not
66
6
        // recording timeout info.
67
6
        //
68
6
        // TODO: decide whether it is a problem that this might let an
69
6
        // observer know whether our stat is read-only.
70
6
        (timeout, timeout)
71
6
    }
72

            
73
2
    fn learning_timeouts(&self) -> bool {
74
2
        false
75
2
    }
76

            
77
2
    fn update_params(&mut self, params: &tor_netdir::params::NetParameters) {
78
2
        self.using_estimates = !bool::from(params.cbt_learning_disabled);
79
2
        self.default_timeout = params
80
2
            .cbt_initial_timeout
81
2
            .try_into()
82
2
            .unwrap_or_else(|_| Duration::from_secs(60));
83
2
    }
84

            
85
    fn build_state(&mut self) -> Option<ParetoTimeoutState> {
86
        None
87
    }
88
}