open_metrics_client/metrics/
counter.rs1use super::{MetricType, TypedMetric};
6use std::marker::PhantomData;
7use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
8use std::sync::Arc;
9
10pub struct Counter<N = u64, A = AtomicU64> {
40 value: Arc<A>,
41 phantom: PhantomData<N>,
42}
43
44impl<N, A> Clone for Counter<N, A> {
45 fn clone(&self) -> Self {
46 Self {
47 value: self.value.clone(),
48 phantom: PhantomData,
49 }
50 }
51}
52
53impl<N, A: Default> Default for Counter<N, A> {
54 fn default() -> Self {
55 Counter {
56 value: Arc::new(A::default()),
57 phantom: PhantomData,
58 }
59 }
60}
61
62impl<N, A: Atomic<N>> Counter<N, A> {
63 pub fn inc(&self) -> N {
65 self.value.inc()
66 }
67
68 pub fn inc_by(&self, v: N) -> N {
70 self.value.inc_by(v)
71 }
72
73 pub fn get(&self) -> N {
75 self.value.get()
76 }
77
78 pub fn inner(&self) -> &A {
87 &self.value
88 }
89}
90
91pub trait Atomic<N> {
92 fn inc(&self) -> N;
93
94 fn inc_by(&self, v: N) -> N;
95
96 fn get(&self) -> N;
97}
98
99impl Atomic<u64> for AtomicU64 {
100 fn inc(&self) -> u64 {
101 self.inc_by(1)
102 }
103
104 fn inc_by(&self, v: u64) -> u64 {
105 self.fetch_add(v, Ordering::Relaxed)
106 }
107
108 fn get(&self) -> u64 {
109 self.load(Ordering::Relaxed)
110 }
111}
112
113impl Atomic<u32> for AtomicU32 {
114 fn inc(&self) -> u32 {
115 self.inc_by(1)
116 }
117
118 fn inc_by(&self, v: u32) -> u32 {
119 self.fetch_add(v, Ordering::Relaxed)
120 }
121
122 fn get(&self) -> u32 {
123 self.load(Ordering::Relaxed)
124 }
125}
126
127impl Atomic<f64> for AtomicU64 {
128 fn inc(&self) -> f64 {
129 self.inc_by(1.0)
130 }
131
132 fn inc_by(&self, v: f64) -> f64 {
133 let mut old_u64 = self.load(Ordering::Relaxed);
134 let mut old_f64;
135 loop {
136 old_f64 = f64::from_bits(old_u64);
137 let new = f64::to_bits(old_f64 + v);
138 match self.compare_exchange_weak(old_u64, new, Ordering::Relaxed, Ordering::Relaxed) {
139 Ok(_) => break,
140 Err(x) => old_u64 = x,
141 }
142 }
143
144 old_f64
145 }
146
147 fn get(&self) -> f64 {
148 f64::from_bits(self.load(Ordering::Relaxed))
149 }
150}
151
152impl<N, A> TypedMetric for Counter<N, A> {
153 const TYPE: MetricType = MetricType::Counter;
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use quickcheck::QuickCheck;
160
161 #[test]
162 fn inc_and_get() {
163 let counter: Counter = Counter::default();
164 assert_eq!(0, counter.inc());
165 assert_eq!(1, counter.get());
166 }
167
168 #[test]
169 fn f64_stored_in_atomic_u64() {
170 fn prop(fs: Vec<f64>) {
171 let fs: Vec<f64> = fs
172 .into_iter()
173 .map(|f| if f.is_normal() { f } else { 0.0 })
175 .collect();
176 let sum = fs.iter().sum();
177 let counter = Counter::<f64, AtomicU64>::default();
178 for f in fs {
179 counter.inc_by(f);
180 }
181 assert_eq!(counter.get(), sum)
182 }
183
184 QuickCheck::new().tests(10).quickcheck(prop as fn(_))
185 }
186}