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

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

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

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

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

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

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

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

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

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

            
59
3
        let multiplier =
60
3
            (action.timeout_scale() as f64) / (reference_action.timeout_scale() as f64);
61
3

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

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

            
74
1
    fn learning_timeouts(&self) -> bool {
75
1
        false
76
1
    }
77

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

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