ticktock/
clock.rs

1//! Frame clock module
2//!
3//! Contains a clocking that ticks in a fixed interval as precisely as
4//! possible.
5
6// FIXME: clock should start immediately, not waiting the initial interval
7
8use std::{iter, thread, time};
9
10/// Clock structure.
11pub struct Clock {
12    /// Start time of the clock, in ns since epoch
13    started_at: time::Instant,
14    /// Tick length
15    tick_len: time::Duration,
16}
17
18/// A clock iterator
19///
20/// Used to iterate over the clock:
21///
22/// ```
23/// use std::time;
24/// use ticktock::Clock;
25///
26/// let start = time::Instant::now();
27/// // ticks once per second
28/// let mut clock = Clock::new(time::Duration::from_secs(1));
29///
30/// // as soon as the clock starts, it will wait for the next tick.
31/// // in this case, we'll start at t = 1 second
32/// for tick in clock.iter() {
33///     // ...
34///
35///     // a simple break will exit
36///     break;
37/// }
38///
39/// let end = time::Instant::now();
40///
41/// assert!(time::Duration::from_secs(1) < end - start);
42/// ```
43pub struct ClockIter<'a>(&'a Clock);
44
45impl Clock {
46    /// Creates a new clock.
47    ///
48    /// Create a clock with a tick size of `tick_len_ms`, in ms.
49    #[inline]
50    pub fn new(tick_len: time::Duration) -> Clock {
51        Clock::new_with_start_time(tick_len, time::Instant::now())
52    }
53
54    /// Creates a new clock with a specified start time
55    #[inline]
56    pub fn new_with_start_time(tick_len: time::Duration, start: time::Instant) -> Clock {
57        Clock {
58            started_at: start,
59            tick_len,
60        }
61    }
62
63    /// Creates a new fixed-framerate clock
64    #[inline]
65    pub fn framerate(fps: f64) -> Clock {
66        Clock::framerate_with_start_time(fps, time::Instant::now())
67    }
68
69    /// Creates a new fixed-framerate clock with a specified sart time
70    #[inline]
71    pub fn framerate_with_start_time(fps: f64, start: time::Instant) -> Clock {
72        let frame_time_s = 1.0 / fps;
73
74        Clock::new_with_start_time(time::Duration::from_secs_f64(frame_time_s), start)
75    }
76
77    /// Creates a new clock with a different tick length that is synced to
78    /// the original clock
79    #[inline]
80    pub fn synced(&self, tick_len: time::Duration) -> Clock {
81        Clock {
82            started_at: self.started_at,
83            tick_len,
84        }
85    }
86
87    /// Get start time
88    #[inline]
89    pub fn started_at(&self) -> time::Instant {
90        self.started_at
91    }
92
93    /// Returns the tick number preceding an specific instant in time
94    #[inline]
95    pub fn tick_num_at(&self, now: time::Instant) -> u128 {
96        (now - self.started_at).as_nanos() / self.tick_len.as_nanos()
97    }
98
99    /// Waits for the next clock tick.
100    ///
101    /// Will wait until the next tick and return the current tick count.
102    #[inline]
103    pub fn wait_until_tick(&self) -> (u128, time::Instant) {
104        // uses signed math because ntp might put us in the past
105        let now = time::Instant::now();
106
107        let current_tick_num = self.tick_num_at(now);
108        let next_tick_num = current_tick_num + 1;
109
110        let next_tick = self.started_at + self.tick_len * next_tick_num as u32;
111        let until_next: time::Duration = next_tick - now;
112
113        thread::sleep(until_next);
114        (next_tick_num, next_tick)
115    }
116
117    /// Creates a clock iterator.
118    ///
119    /// The iterator will iterate forever, calling `wait_until_tick` on each
120    /// iteration. It will panic after about 293 years.
121    ///
122    /// Returns (current tick number, absolute time) on each iteration, where
123    /// absolute time is relative to a fixed offset that depends on the machine
124    /// (see `Instant`).
125    #[inline]
126    pub fn iter(&self) -> ClockIter {
127        ClockIter(self)
128    }
129
130    /// Create a relative clock iterator.
131    ///
132    /// Similar to `iter()`, but the resulting iterator will return a tuple of
133    /// (current tick number, relative time), with relative time being a
134    /// `time::Duration` from the start of the clock.
135    #[inline]
136    pub fn rel_iter(&self) -> ClockIterRelative {
137        ClockIterRelative(self)
138    }
139}
140
141impl<'a> iter::Iterator for ClockIter<'a> {
142    type Item = (u128, time::Instant);
143
144    #[inline]
145    fn next(&mut self) -> Option<Self::Item> {
146        Some(self.0.wait_until_tick())
147    }
148}
149
150/// Similar to `ClockIter`, but returns a relative time instead.
151///
152/// The resulting returned tuple will be of the form `(tick_number,
153/// duration_since_clock_start)`
154pub struct ClockIterRelative<'a>(&'a Clock);
155
156impl<'a> iter::Iterator for ClockIterRelative<'a> {
157    type Item = (u128, time::Duration);
158
159    #[inline]
160    fn next(&mut self) -> Option<Self::Item> {
161        let (n, t) = self.0.wait_until_tick();
162        Some((n, t - self.0.started_at))
163    }
164}