1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use time::{SteadyTime, Timespec, get_time};

use std::ops::Add;
use std::time::Duration;

/// The current time
///
/// This value is similar to (and directly derived from) the
/// `time::SteadyTime`.  But it has three important properties:
///
/// 1. It has a millisecond precision
/// 2. It's size is 8 bytes (SteadyTime is 16 bytes)
/// 3. It supports math with `std::time::Duration` (more future-proof)
///
/// The size of the value is important because we are going to have a lot of
/// timeouts and lots of timestamps stored inside the state machines.
///
/// Precision of millisecond is good enough, and even better for our use case
/// as it allows faster comparison and more frequent matches.
///
/// Warning: when adding a duration that is not a multiple of a millisecond
/// we truncate (i.e. floor) the duration value. We may change this in future.
/// Note that for timeouts this works well enough, as mio already bumps the
/// timeout to at least a millisecond ahead.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Time(u64);

impl Add<Duration> for Time {
    type Output = Time;
    fn add(self, rhs: Duration) -> Time {
        Time(self.0 + rhs.as_secs()*1000 + (rhs.subsec_nanos()/1000000) as u64)
    }
}

impl Time {
    /// Zero time value, should be used only as a starting point for unit
    /// tests
    pub fn zero() -> Time {
        // In fact we don't care actual value, but the 1 allows us to
        // implement NonZero in the future
        Time(1)
    }
}

pub fn make_time(base: SteadyTime, now: SteadyTime) -> Time {
    Time((now - base).num_milliseconds() as u64
         // Time starts with 1 not with zero
         + 1)
}

pub fn mio_timeout_ms(now: Time, event: Time) -> u64 {
    if event.0 > now.0 {
        // We need +1 because we truncate both old and new timeouts to
        // millisecond precision, while mio calculates at the nanosecond
        // precision (but doesn't expose it). So wake up time may be up
        // to a millisecond smaller then expected
        event.0 - now.0 + 1
    } else {
        0
    }
}

pub fn estimate_timespec(now: Time, value: Time) -> Timespec {
    get_time() + ::time::Duration::milliseconds(value.0 as i64 - now.0 as i64)
}


#[cfg(test)]
mod test {
    use super::Time;
    use std::time::Duration;


    #[test]
    fn test_add_duration() {
        let tm = Time::zero();
        assert_eq!(tm + Duration::new(10, 0), Time(10001));
        assert_eq!(tm + Duration::from_millis(150), Time(151));
        assert_eq!(tm + Duration::from_millis(12345), Time(12346));
        assert_eq!(tm + Duration::new(5, 0) + Duration::from_millis(20),
                   Time(5021));
    }

}