1use std::time::{Duration, Instant as StdInstant};
23#[cfg(test)]
4use std::sync::Arc;
56#[cfg(test)]
7use parking_lot::RwLock;
89// This is `moka`'s `Instant` struct.
10use super::Instant;
1112#[derive(Default, Clone)]
13pub(crate) struct Clock {
14 ty: ClockType,
15}
1617#[derive(Clone)]
18enum ClockType {
19/// A clock that uses `std::time::Instant` as the source of time.
20Standard { origin: StdInstant },
21#[cfg(feature = "quanta")]
22/// A clock that uses both `std::time::Instant` and `quanta::Instant` as the
23 /// sources of time.
24Hybrid {
25 std_origin: StdInstant,
26 quanta_origin: quanta::Instant,
27 },
28#[cfg(test)]
29/// A clock that uses a mocked source of time.
30Mocked { mock: Arc<Mock> },
31}
3233impl Default for ClockType {
34/// Create a new `ClockType` with the current time as the origin.
35 ///
36 /// If the `quanta` feature is enabled, `Hybrid` will be used. Otherwise,
37 /// `Standard` will be used.
38fn default() -> Self {
39#[cfg(feature = "quanta")]
40{
41return ClockType::Hybrid {
42 std_origin: StdInstant::now(),
43 quanta_origin: quanta::Instant::now(),
44 };
45 }
4647#[allow(unreachable_code)]
48ClockType::Standard {
49 origin: StdInstant::now(),
50 }
51 }
52}
5354impl Clock {
55#[cfg(test)]
56/// Creates a new `Clock` with a mocked source of time.
57pub(crate) fn mock() -> (Clock, Arc<Mock>) {
58let mock = Arc::new(Mock::default());
59let clock = Clock {
60 ty: ClockType::Mocked {
61 mock: Arc::clone(&mock),
62 },
63 };
64 (clock, mock)
65 }
6667/// Returns the current time using a reliable source of time.
68 ///
69 /// When the the type is `Standard` or `Hybrid`, the time is based on
70 /// `std::time::Instant`. When the type is `Mocked`, the time is based on the
71 /// mocked source of time.
72pub(crate) fn now(&self) -> Instant {
73match &self.ty {
74 ClockType::Standard { origin } => {
75 Instant::from_duration_since_clock_start(origin.elapsed())
76 }
77#[cfg(feature = "quanta")]
78ClockType::Hybrid { std_origin, .. } => {
79 Instant::from_duration_since_clock_start(std_origin.elapsed())
80 }
81#[cfg(test)]
82ClockType::Mocked { mock } => Instant::from_duration_since_clock_start(mock.elapsed()),
83 }
84 }
8586/// Returns the current time _maybe_ using a fast but less reliable source of
87 /// time. The time may drift from the time returned by `now`, or not be
88 /// monotonically increasing.
89 ///
90 /// This is useful for performance critical code that does not require the same
91 /// level of precision as `now`. (e.g. measuring the time between two events for
92 /// metrics)
93 ///
94 /// When the type is `Standard` or `Mocked`, `now` is internally called. So there
95 /// is no performance benefit.
96 ///
97 /// When the type is `Hybrid`, the time is based on `quanta::Instant`, which can
98 /// be faster than `std::time::Instant`, depending on the CPU architecture.
99pub(crate) fn fast_now(&self) -> Instant {
100match &self.ty {
101#[cfg(feature = "quanta")]
102ClockType::Hybrid { quanta_origin, .. } => {
103 Instant::from_duration_since_clock_start(quanta_origin.elapsed())
104 }
105 ClockType::Standard { .. } => self.now(),
106#[cfg(test)]
107ClockType::Mocked { .. } => self.now(),
108 }
109 }
110111/// Converts an `Instant` to a `std::time::Instant`.
112 ///
113 /// **IMPORTANT**: The caller must ensure that the `Instant` was created by this
114 /// `Clock`, otherwise the resulting `std::time::Instant` will be incorrect.
115pub(crate) fn to_std_instant(&self, instant: Instant) -> StdInstant {
116match &self.ty {
117 ClockType::Standard { origin } => {
118let duration = Duration::from_nanos(instant.as_nanos());
119*origin + duration
120 }
121#[cfg(feature = "quanta")]
122ClockType::Hybrid { std_origin, .. } => {
123let duration = Duration::from_nanos(instant.as_nanos());
124*std_origin + duration
125 }
126#[cfg(test)]
127ClockType::Mocked { mock } => {
128let duration = Duration::from_nanos(instant.as_nanos());
129 mock.origin + duration
130 }
131 }
132 }
133}
134135#[cfg(test)]
136pub(crate) struct Mock {
137 origin: StdInstant,
138 now: RwLock<StdInstant>,
139}
140141#[cfg(test)]
142impl Default for Mock {
143fn default() -> Self {
144let origin = StdInstant::now();
145Self {
146 origin,
147 now: RwLock::new(origin),
148 }
149 }
150}
151152#[cfg(test)]
153impl Mock {
154pub(crate) fn increment(&self, amount: Duration) {
155*self.now.write() += amount;
156 }
157158pub(crate) fn elapsed(&self) -> Duration {
159self.now.read().duration_since(self.origin)
160 }
161}