waffles_solana_sdk/
timing.rs1use {
3 crate::unchecked_div_by_const,
4 std::{
5 sync::atomic::{AtomicU64, Ordering},
6 time::{Duration, SystemTime, UNIX_EPOCH},
7 },
8};
9
10pub fn duration_as_ns(d: &Duration) -> u64 {
11 d.as_secs()
12 .saturating_mul(1_000_000_000)
13 .saturating_add(u64::from(d.subsec_nanos()))
14}
15
16pub fn duration_as_us(d: &Duration) -> u64 {
17 d.as_secs()
18 .saturating_mul(1_000_000)
19 .saturating_add(unchecked_div_by_const!(u64::from(d.subsec_nanos()), 1_000))
20}
21
22pub fn duration_as_ms(d: &Duration) -> u64 {
23 d.as_secs()
24 .saturating_mul(1000)
25 .saturating_add(unchecked_div_by_const!(
26 u64::from(d.subsec_nanos()),
27 1_000_000
28 ))
29}
30
31pub fn duration_as_s(d: &Duration) -> f32 {
32 d.as_secs() as f32 + (d.subsec_nanos() as f32 / 1_000_000_000.0)
33}
34
35pub fn timestamp() -> u64 {
37 let now = SystemTime::now()
38 .duration_since(UNIX_EPOCH)
39 .expect("create timestamp in timing");
40 duration_as_ms(&now)
41}
42
43pub const SECONDS_PER_YEAR: f64 = 365.242_199 * 24.0 * 60.0 * 60.0;
44
45pub fn years_as_slots(years: f64, tick_duration: &Duration, ticks_per_slot: u64) -> f64 {
47 years *
49 SECONDS_PER_YEAR
51 * (1_000_000_000.0 / duration_as_ns(tick_duration) as f64)
53 / ticks_per_slot as f64
55}
56
57pub fn slot_duration_from_slots_per_year(slots_per_year: f64) -> Duration {
59 let slot_in_ns = if slots_per_year != 0.0 {
61 (SECONDS_PER_YEAR * 1_000_000_000.0) / slots_per_year
62 } else {
63 0.0
64 };
65 Duration::from_nanos(slot_in_ns as u64)
66}
67
68#[derive(Debug, Default)]
69pub struct AtomicInterval {
70 last_update: AtomicU64,
71}
72
73impl AtomicInterval {
74 pub fn should_update(&self, interval_time_ms: u64) -> bool {
76 self.should_update_ext(interval_time_ms, true)
77 }
78
79 pub fn should_update_ext(&self, interval_time_ms: u64, skip_first: bool) -> bool {
83 let now = timestamp();
84 let last = self.last_update.load(Ordering::Relaxed);
85 now.saturating_sub(last) > interval_time_ms
86 && self
87 .last_update
88 .compare_exchange(last, now, Ordering::Relaxed, Ordering::Relaxed)
89 == Ok(last)
90 && !(skip_first && last == 0)
91 }
92
93 pub fn elapsed_ms(&self) -> u64 {
95 let now = timestamp();
96 let last = self.last_update.load(Ordering::Relaxed);
97 now.saturating_sub(last) }
99
100 pub fn remaining_until_next_interval(&self, interval_time: u64) -> u64 {
102 interval_time.saturating_sub(self.elapsed_ms())
103 }
104}
105
106#[cfg(test)]
107mod test {
108 use super::*;
109
110 #[test]
111 fn test_interval_update() {
112 solana_logger::setup();
113 let i = AtomicInterval::default();
114 assert!(!i.should_update(1000));
115
116 let i = AtomicInterval::default();
117 assert!(i.should_update_ext(1000, false));
118
119 std::thread::sleep(Duration::from_millis(10));
120 assert!(i.elapsed_ms() > 9 && i.elapsed_ms() < 1000);
121 assert!(
122 i.remaining_until_next_interval(1000) > 9
123 && i.remaining_until_next_interval(1000) < 991
124 );
125 assert!(i.should_update(9));
126 assert!(!i.should_update(100));
127 }
128
129 #[test]
130 #[allow(clippy::float_cmp)]
131 fn test_years_as_slots() {
132 let tick_duration = Duration::from_micros(1000 * 1000 / 160);
133
134 assert_eq!(years_as_slots(0.0, &tick_duration, 4) as u64, 0);
136 assert_eq!(
137 years_as_slots(1.0 / 12f64, &tick_duration, 4) as u64,
138 105_189_753
139 );
140 assert_eq!(years_as_slots(1.0, &tick_duration, 4) as u64, 1_262_277_039);
141
142 let tick_duration = Duration::from_micros(1000 * 1000);
143 assert_eq!(
145 years_as_slots(1.0 / SECONDS_PER_YEAR, &tick_duration, 1),
146 1.0
147 );
148 }
149
150 #[test]
151 fn test_slot_duration_from_slots_per_year() {
152 let slots_per_year = 1_262_277_039.0;
153 let ticks_per_slot = 4;
154
155 assert_eq!(
156 slot_duration_from_slots_per_year(slots_per_year),
157 Duration::from_micros(1000 * 1000 / 160) * ticks_per_slot
158 );
159 assert_eq!(
160 slot_duration_from_slots_per_year(0.0),
161 Duration::from_micros(0) * ticks_per_slot
162 );
163
164 let slots_per_year = SECONDS_PER_YEAR;
165 let ticks_per_slot = 1;
166 assert_eq!(
167 slot_duration_from_slots_per_year(slots_per_year),
168 Duration::from_millis(1000) * ticks_per_slot
169 );
170 }
171}