1use std::{
12 sync::atomic::{AtomicU64, Ordering},
13 time::{Duration, SystemTime},
14};
15
16#[derive(Debug)]
18pub enum SampleRate {
19 Duration(Duration),
22 Frequency(u64),
26 Always,
28}
29
30pub struct Sampling {
33 rate: SampleRate,
34 state: AtomicU64,
35}
36
37impl Sampling {
38 pub const fn new(rate: SampleRate) -> Self {
39 Self {
40 rate,
41 state: AtomicU64::new(0),
42 }
43 }
44
45 pub fn sample(&self) -> bool {
46 match &self.rate {
47 SampleRate::Duration(rate) => {
48 Self::sample_duration(rate, &self.state)
49 }
50 SampleRate::Frequency(rate) => {
51 Self::sample_frequency(*rate, &self.state)
52 }
53 SampleRate::Always => true,
54 }
55 }
56
57 fn sample_frequency(rate: u64, count: &AtomicU64) -> bool {
58 let previous_count = count
59 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |count| {
60 let new_count = if count == 0 {
61 rate.saturating_sub(1)
62 } else {
63 count.saturating_sub(1)
64 };
65 Some(new_count)
66 })
67 .expect("Closure should always returns 'Some'. This is a Bug.");
68
69 previous_count == 0
70 }
71
72 fn sample_duration(rate: &Duration, last_sample: &AtomicU64) -> bool {
73 let rate = rate.as_secs();
74 let now = SystemTime::now()
76 .duration_since(SystemTime::UNIX_EPOCH)
77 .expect("SystemTime before UNIX EPOCH!")
78 .as_secs();
79
80 last_sample
81 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |last_sample| {
82 if now.saturating_sub(last_sample) >= rate {
83 Some(now)
84 } else {
85 None
86 }
87 })
88 .is_ok()
89 }
90}
91
92#[macro_export]
95macro_rules! sample {
96 ($sample_rate:expr, $($args:expr)+ ,) => {
97 $crate::sample!($sample_rate, $($args)+);
98 };
99
100 ($sample_rate:expr, $($args:tt)+) => {{
101 static SAMPLING: Sampling = $crate::sample::Sampling::new($sample_rate);
102 if SAMPLING.sample() {
103 $($args)+
104 }
105 }};
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn frequency() {
114 let sampling = Sampling::new(SampleRate::Frequency(10));
116 let mut v = Vec::new();
117 for i in 0..=25 {
118 if sampling.sample() {
119 v.push(i);
120 }
121 }
122
123 assert_eq!(v, vec![0, 10, 20]);
124 }
125
126 #[test]
127 fn always() {
128 let sampling = Sampling::new(SampleRate::Always);
130 let mut v = Vec::new();
131 for i in 0..5 {
132 if sampling.sample() {
133 v.push(i);
134 }
135 }
136
137 assert_eq!(v, vec![0, 1, 2, 3, 4]);
138 }
139
140 #[ignore]
141 #[test]
142 fn duration() {
143 let sampling =
145 Sampling::new(SampleRate::Duration(Duration::from_secs(1)));
146 let mut v = Vec::new();
147 for i in 0..5 {
148 if sampling.sample() {
149 v.push(i);
150 }
151
152 std::thread::sleep(Duration::from_millis(500));
153 }
154
155 assert_eq!(v.len(), 2);
156 }
157
158 #[test]
159 fn macro_expansion() {
160 for i in 0..10 {
161 sample!(
162 SampleRate::Frequency(2),
163 println!("loooooooooooooooooooooooooong hello {}", i),
164 );
165
166 sample!(SampleRate::Frequency(2), {
167 println!("hello {}", i);
168 });
169
170 sample!(SampleRate::Frequency(2), println!("hello {}", i));
171
172 sample! {
173 SampleRate::Frequency(2),
174
175 for j in 10..20 {
176 println!("hello {}", j);
177 }
178 }
179 }
180 }
181
182 #[test]
183 fn threaded() {
184 fn work() -> usize {
185 let mut count = 0;
186
187 for _ in 0..1000 {
188 sample!(SampleRate::Frequency(5), count += 1);
189 }
190
191 count
192 }
193
194 let mut handles = Vec::new();
195 for _ in 0..10 {
196 handles.push(std::thread::spawn(work));
197 }
198
199 let mut count = 0;
200 for handle in handles {
201 count += handle.join().unwrap();
202 }
203
204 assert_eq!(count, 2000);
205 }
206}