sched_clock/
lib.rs

1//! Representations of time needed by task scheduling algorithms
2//!
3//! For the purpose of task scheduling, we represent time as a 64-bit signed
4//! count of nanoseconds before or after some reference (such as system startup,
5//! scheduler startup...). This seems appropriate as of 2019 because:
6//!
7//! - CPU frequencies are currently capped at a few GHz by thermal limits, and
8//!   no known physics can help us evade those limits, so a nanosecond is likely
9//!   to remain a few CPU cycles for a while. Which is enough precision for any
10//!   practical scheduling purpose, because the scheduler itself will take more
11//!   time to execute / have more jitter than that.
12//! - 2^63 nanoseconds is a bit less than 300 years, and as of 2019 humans are
13//!   not capable of planning work several centuries ahead or work that will
14//!   take several centuries to complete. Not to mention that this code will
15//!   likely become obsolete on a much smaller timescale, of course.
16//!
17//! We're largely reimplementing `std::time::{Duration, Instant}` here, because
18//! while we like its overall API design...
19//!
20//! - We do not need the huge dynamic range of std::time (~600 years is enough
21//!   for us). And we expect to manipulate time often and under tight
22//!   performance constraints, so we need all the extra processing speed and
23//!   storage savings that we can get.
24//! - We want freedom to switch to OS- or hardware-provided facilities that give
25//!   us either more correctness, more speed, or both, whereas the standard
26//!   library puts portability and simplicity above everything else.
27//! - We find signed durations and instants to be useful and worth supporting.
28
29#![deny(missing_docs)]
30
31pub mod clocks;
32pub mod prelude;
33
34use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
35
36pub use crate::clocks::{Clock, DefaultClock};
37
38/// Points in time
39///
40/// Instants (aka timestamps) are encoded as a number of nanoseconds
41/// since/before some epoch, and can be measured using a Clock.
42///
43/// There are many possible choices of epoch, including process startup time,
44/// system startup time, and UTC references. The choice of epoch is specific to
45/// a given Clock implementation, and _may_ be spelled out in that Clock's
46/// documentation when the implementor knows about it.
47///
48#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
49pub struct Instant(i64);
50//
51impl Instant {
52    /// Instants are defined relatively to an epoch
53    pub const EPOCH: Self = Self(0);
54
55    /// "Minus infinity" instant, as far away in the past as representable
56    pub const FOREVER_AGO: Self = Self(i64::min_value());
57
58    /// "Plus infinity" instant, as far away in the future as representable
59    pub const SOMEDAY: Self = Self(i64::max_value());
60}
61
62/// Algebraic (signed) durations
63///
64/// Represents a span of wall-clock time, the difference between two instants.
65/// May be negative to represent going backwards in time.
66///
67#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
68pub struct Duration(i64);
69//
70impl Duration {
71    /// One nanosecond, i.e. one billionth of a second
72    pub const NANOSECOND: Self = Self(1);
73
74    /// One microsecond, i.e. one millionth of a second
75    pub const MICROSECOND: Self = Self(1000 * Self::NANOSECOND.0);
76
77    /// One millisecond, i.e. one thousandth of a second
78    pub const MILLISECOND: Self = Self(1000 * Self::MICROSECOND.0);
79
80    /// One second
81    pub const SECOND: Self = Self(1000 * Self::MILLISECOND.0);
82
83    /// One minute
84    pub const MINUTE: Self = Self(60 * Self::SECOND.0);
85
86    /// One hour
87    pub const HOUR: Self = Self(60 * Self::MINUTE.0);
88
89    // NOTE: We can't go beyond that because...
90    //       - Not all days are 24 hours (see: daylight saving times)
91    //       - Not all months are 30 days
92    //       - The duration of a year is a pain to spell out, and rarely useful
93
94    /// "Infinity" duration, as long as representable
95    pub const FOREVER: Self = Self(i64::max_value());
96
97    /// Create a new Duration from a number of nanoseconds
98    pub const fn from_nanos(nanos: i64) -> Self {
99        Self(nanos)
100    }
101
102    /// Turn into a number of signed integer nanoseconds
103    pub const fn as_nanos(self) -> i64 {
104        self.0
105    }
106}
107
108// Basic arithmetic operations
109impl Add<Duration> for Instant {
110    type Output = Instant;
111
112    fn add(self, rhs: Duration) -> Instant {
113        Instant(self.0 + rhs.0)
114    }
115}
116//
117impl Add<Duration> for Duration {
118    type Output = Duration;
119
120    fn add(self, rhs: Duration) -> Duration {
121        Duration(self.0 + rhs.0)
122    }
123}
124//
125impl Div<i64> for Duration {
126    type Output = Duration;
127
128    fn div(self, rhs: i64) -> Duration {
129        Duration(self.0 / rhs)
130    }
131}
132//
133impl Mul<Duration> for i64 {
134    type Output = Duration;
135
136    fn mul(self, rhs: Duration) -> Duration {
137        Duration(self * rhs.0)
138    }
139}
140//
141impl Mul<i64> for Duration {
142    type Output = Duration;
143
144    fn mul(self, rhs: i64) -> Duration {
145        Duration(self.0 * rhs)
146    }
147}
148//
149impl Neg for Duration {
150    type Output = Duration;
151
152    fn neg(self) -> Duration {
153        Duration(-self.0)
154    }
155}
156//
157impl Sub<Duration> for Instant {
158    type Output = Instant;
159
160    fn sub(self, rhs: Duration) -> Instant {
161        Instant(self.0 - rhs.0)
162    }
163}
164//
165impl Sub<Duration> for Duration {
166    type Output = Duration;
167
168    fn sub(self, rhs: Duration) -> Duration {
169        Duration(self.0 - rhs.0)
170    }
171}
172//
173impl Sub<Instant> for Instant {
174    type Output = Duration;
175
176    fn sub(self, rhs: Instant) -> Duration {
177        Duration(self.0 - rhs.0)
178    }
179}
180
181// In-place arithmetic operations
182impl AddAssign<Duration> for Instant {
183    fn add_assign(&mut self, other: Duration) {
184        self.0 += other.0;
185    }
186}
187//
188impl AddAssign<Duration> for Duration {
189    fn add_assign(&mut self, other: Duration) {
190        self.0 += other.0;
191    }
192}
193//
194impl DivAssign<i64> for Duration {
195    fn div_assign(&mut self, other: i64) {
196        self.0 /= other
197    }
198}
199//
200impl MulAssign<i64> for Duration {
201    fn mul_assign(&mut self, other: i64) {
202        self.0 *= other;
203    }
204}
205//
206impl SubAssign<Duration> for Duration {
207    fn sub_assign(&mut self, other: Duration) {
208        self.0 -= other.0;
209    }
210}
211//
212impl SubAssign<Duration> for Instant {
213    fn sub_assign(&mut self, other: Duration) {
214        self.0 -= other.0;
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221    use quickcheck::{Arbitrary, Gen};
222    use quickcheck_macros::quickcheck;
223    use std::convert::TryInto;
224
225    /// "Minus infinity" is useful in tests, but I'm not sure if we want to expose it yet
226    const MINUS_FOREVER: Duration = Duration(i64::min_value());
227
228    // Traditional special-case testing
229
230    #[test]
231    /// Associated consts of Instant
232    fn special_instants() {
233        // Simple ordering test
234        assert!(Instant::FOREVER_AGO < Instant::EPOCH);
235        assert!(Instant::FOREVER_AGO < Instant::SOMEDAY);
236        assert!(Instant::EPOCH < Instant::SOMEDAY);
237
238        // Relationships between infinite instants and infinite durations
239        assert_eq!(Instant::SOMEDAY - Instant::EPOCH, Duration::FOREVER);
240        assert_eq!(Instant::FOREVER_AGO - Instant::EPOCH, MINUS_FOREVER);
241    }
242
243    #[test]
244    /// Associated consts of Duration
245    fn special_durations() {
246        // We don't really _need_ these properties, but we use them in tests
247        assert_eq!(Duration::default(), Duration(0));
248        assert_eq!(Duration::NANOSECOND, Duration(1));
249
250        // Nanosecond should validate usual properties
251        assert!(Duration::NANOSECOND > Duration(0));
252        assert_eq!(Duration::NANOSECOND, Duration::from_nanos(1));
253        assert_eq!(Duration::NANOSECOND.as_nanos(), 1);
254
255        // Left-Mul should work and validate usual relationships
256        assert_eq!(Duration::MICROSECOND, 1000 * Duration::NANOSECOND);
257        assert_eq!(Duration::MILLISECOND, 1000 * Duration::MICROSECOND);
258        assert_eq!(Duration::SECOND, 1000 * Duration::MILLISECOND);
259        assert_eq!(Duration::MINUTE, 60 * Duration::SECOND);
260        assert_eq!(Duration::HOUR, 60 * Duration::MINUTE);
261
262        // Infinity is far, far away
263        assert!(Duration::HOUR < Duration::FOREVER);
264    }
265
266    // Property-based testing
267
268    impl Arbitrary for Duration {
269        fn arbitrary<G: Gen>(g: &mut G) -> Self {
270            Duration(<i64 as Arbitrary>::arbitrary::<G>(g))
271        }
272    }
273
274    impl Arbitrary for Instant {
275        fn arbitrary<G: Gen>(g: &mut G) -> Self {
276            Instant(<i64 as Arbitrary>::arbitrary::<G>(g))
277        }
278    }
279
280    #[quickcheck]
281    /// Properties shared by all Duration
282    fn duration_properties(d: Duration) -> bool {
283        // These properties should always hold
284        let mut dt0 = d;
285        dt0 *= 0;
286        let mut dt1 = d;
287        dt1 *= 1;
288        let mut dd1 = d;
289        dd1 /= 1;
290        let always_check = d >= MINUS_FOREVER
291            && d <= Duration::FOREVER
292            && 0 * d == Duration::default()
293            && d * 0 == 0 * d
294            && dt0 == 0 * d
295            && 1 * d == d
296            && d * 1 == 1 * d
297            && dt1 == 1 * d
298            && d / 1 == d
299            && dd1 == d / 1
300            && Duration::from_nanos(d.as_nanos()) == d;
301
302        // Since -{signed}::min_value() doesn't exist, we must special-case
303        // before testing properties of the negation
304        if d == MINUS_FOREVER {
305            return always_check;
306        }
307        let mut dtm1 = d;
308        dtm1 *= -1;
309        let mut ddm1 = d;
310        ddm1 /= -1;
311        (-d != d || d.as_nanos() == 0)
312            && (-1) * d == -d
313            && d * (-1) == -d
314            && d / (-1) == -d
315            && dtm1 == -d
316            && ddm1 == -d
317            && -(-d) == d
318    }
319
320    #[quickcheck]
321    /// Properties shared by all (Duration, Duration) pairs
322    fn duration_duration_properties(d1: Duration, d2: Duration) -> bool {
323        let sum = (i128::from(d1.as_nanos()) + i128::from(d2.as_nanos()))
324            .try_into()
325            .map(Duration::from_nanos);
326        let sum_test = if let Ok(sum) = sum {
327            let mut d1p2 = d1;
328            d1p2 += d2;
329            d1 + d2 == sum && d1p2 == d1 + d2
330        } else {
331            true
332        };
333
334        let diff = (i128::from(d1.as_nanos()) - i128::from(d2.as_nanos()))
335            .try_into()
336            .map(Duration::from_nanos);
337        let diff_test = if let Ok(diff) = diff {
338            let mut d1m2 = d1;
339            d1m2 -= d2;
340            d1 - d2 == diff && d1m2 == d1 - d2
341        } else {
342            true
343        };
344
345        sum_test && diff_test
346    }
347
348    #[quickcheck]
349    /// Properties shared by all (Duration, i64) pairs
350    fn duration_i64_properties(d: Duration, i: i64) -> bool {
351        let mul = (i128::from(d.as_nanos()) * i128::from(i))
352            .try_into()
353            .map(Duration::from_nanos);
354        let mul_test = if let Ok(mul) = mul {
355            let mut dti = d;
356            dti *= i;
357            d * i == mul && i * d == d * i && dti == d * i
358        } else {
359            true
360        };
361
362        if i == 0 {
363            return mul_test;
364        }
365        let div = (i128::from(d.as_nanos()) / i128::from(i))
366            .try_into()
367            .map(Duration::from_nanos);
368        let div_test = if let Ok(div) = div {
369            let mut doi = d;
370            doi /= i;
371            d / i == div && doi == d / i
372        } else {
373            true
374        };
375
376        mul_test && div_test
377    }
378
379    #[quickcheck]
380    /// Properties shared by all Instants
381    fn instant_properties(i: Instant) -> bool {
382        i >= Instant::FOREVER_AGO && i <= Instant::SOMEDAY
383    }
384
385    #[quickcheck]
386    /// Properties shared by all (Instant, Duration) pairs
387    fn instant_duration_properties(i: Instant, d: Duration) -> bool {
388        let sum = (i128::from(i.0) + i128::from(d.as_nanos()))
389            .try_into()
390            .map(Instant);
391        let sum_test = if let Ok(sum) = sum {
392            let mut ipd = i;
393            ipd += d;
394            i + d == sum && ipd == i + d
395        } else {
396            true
397        };
398
399        let diff = (i128::from(i.0) - i128::from(d.as_nanos()))
400            .try_into()
401            .map(Instant);
402        let diff_test = if let Ok(diff) = diff {
403            let mut imd = i;
404            imd -= d;
405            i - d == diff && imd == i - d
406        } else {
407            true
408        };
409
410        sum_test && diff_test
411    }
412
413    #[quickcheck]
414    /// Properties shared by all (Instant, Instant) pairs
415    fn instant_instant_properties(i1: Instant, i2: Instant) -> bool {
416        let diff = (i128::from(i1.0) - i128::from(i2.0))
417            .try_into()
418            .map(Duration::from_nanos);
419        if let Ok(diff) = diff {
420            i1 - i2 == diff
421        } else {
422            true
423        }
424    }
425}