prometheus_client/metrics/
counter.rs1use crate::encoding::{EncodeMetric, MetricEncoder, NoLabelSet};
6
7use super::{MetricType, TypedMetric};
8use std::marker::PhantomData;
9#[cfg(target_has_atomic = "64")]
10use std::sync::atomic::AtomicU64;
11use std::sync::atomic::{AtomicU32, Ordering};
12use std::sync::Arc;
13
14#[cfg(target_has_atomic = "64")]
44#[derive(Debug)]
45pub struct Counter<N = u64, A = AtomicU64> {
46 value: Arc<A>,
47 phantom: PhantomData<N>,
48}
49
50#[cfg(not(target_has_atomic = "64"))]
52#[derive(Debug)]
53pub struct Counter<N = u32, A = AtomicU32> {
54 value: Arc<A>,
55 phantom: PhantomData<N>,
56}
57
58impl<N, A> Clone for Counter<N, A> {
59 fn clone(&self) -> Self {
60 Self {
61 value: self.value.clone(),
62 phantom: PhantomData,
63 }
64 }
65}
66
67impl<N, A: Default> Default for Counter<N, A> {
68 fn default() -> Self {
69 Counter {
70 value: Arc::new(A::default()),
71 phantom: PhantomData,
72 }
73 }
74}
75
76impl<N, A: Atomic<N>> Counter<N, A> {
77 pub fn inc(&self) -> N {
79 self.value.inc()
80 }
81
82 pub fn inc_by(&self, v: N) -> N {
84 self.value.inc_by(v)
85 }
86
87 pub fn get(&self) -> N {
89 self.value.get()
90 }
91
92 pub fn inner(&self) -> &A {
101 &self.value
102 }
103}
104
105pub trait Atomic<N> {
107 fn inc(&self) -> N;
109
110 fn inc_by(&self, v: N) -> N;
112
113 fn get(&self) -> N;
115}
116
117#[cfg(target_has_atomic = "64")]
118impl Atomic<u64> for AtomicU64 {
119 fn inc(&self) -> u64 {
120 self.inc_by(1)
121 }
122
123 fn inc_by(&self, v: u64) -> u64 {
124 self.fetch_add(v, Ordering::Relaxed)
125 }
126
127 fn get(&self) -> u64 {
128 self.load(Ordering::Relaxed)
129 }
130}
131
132impl Atomic<u32> for AtomicU32 {
133 fn inc(&self) -> u32 {
134 self.inc_by(1)
135 }
136
137 fn inc_by(&self, v: u32) -> u32 {
138 self.fetch_add(v, Ordering::Relaxed)
139 }
140
141 fn get(&self) -> u32 {
142 self.load(Ordering::Relaxed)
143 }
144}
145
146#[cfg(target_has_atomic = "64")]
147impl Atomic<f64> for AtomicU64 {
148 fn inc(&self) -> f64 {
149 self.inc_by(1.0)
150 }
151
152 fn inc_by(&self, v: f64) -> f64 {
153 let mut old_u64 = self.load(Ordering::Relaxed);
154 let mut old_f64;
155 loop {
156 old_f64 = f64::from_bits(old_u64);
157 let new = f64::to_bits(old_f64 + v);
158 match self.compare_exchange_weak(old_u64, new, Ordering::Relaxed, Ordering::Relaxed) {
159 Ok(_) => break,
160 Err(x) => old_u64 = x,
161 }
162 }
163
164 old_f64
165 }
166
167 fn get(&self) -> f64 {
168 f64::from_bits(self.load(Ordering::Relaxed))
169 }
170}
171
172impl Atomic<f32> for AtomicU32 {
173 fn inc(&self) -> f32 {
174 self.inc_by(1.0)
175 }
176
177 fn inc_by(&self, v: f32) -> f32 {
178 let mut old_u32 = self.load(Ordering::Relaxed);
179 let mut old_f32;
180 loop {
181 old_f32 = f32::from_bits(old_u32);
182 let new = f32::to_bits(old_f32 + v);
183 match self.compare_exchange_weak(old_u32, new, Ordering::Relaxed, Ordering::Relaxed) {
184 Ok(_) => break,
185 Err(x) => old_u32 = x,
186 }
187 }
188
189 old_f32
190 }
191
192 fn get(&self) -> f32 {
193 f32::from_bits(self.load(Ordering::Relaxed))
194 }
195}
196
197impl<N, A> TypedMetric for Counter<N, A> {
198 const TYPE: MetricType = MetricType::Counter;
199}
200
201impl<N, A> EncodeMetric for Counter<N, A>
202where
203 N: crate::encoding::EncodeCounterValue,
204 A: Atomic<N>,
205{
206 fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
207 encoder.encode_counter::<NoLabelSet, _, u64>(&self.get(), None)
208 }
209
210 fn metric_type(&self) -> MetricType {
211 Self::TYPE
212 }
213}
214
215#[derive(Debug, Default)]
219pub struct ConstCounter<N = u64> {
220 value: N,
221}
222
223impl<N> ConstCounter<N> {
224 pub fn new(value: N) -> Self {
226 Self { value }
227 }
228}
229
230impl<N> TypedMetric for ConstCounter<N> {
231 const TYPE: MetricType = MetricType::Counter;
232}
233
234impl<N> EncodeMetric for ConstCounter<N>
235where
236 N: crate::encoding::EncodeCounterValue,
237{
238 fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
239 encoder.encode_counter::<NoLabelSet, _, u64>(&self.value, None)
240 }
241
242 fn metric_type(&self) -> MetricType {
243 Self::TYPE
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use quickcheck::QuickCheck;
251
252 #[test]
253 fn inc_and_get() {
254 let counter: Counter = Counter::default();
255 assert_eq!(0, counter.inc());
256 assert_eq!(1, counter.get());
257 }
258
259 #[cfg(target_has_atomic = "64")]
260 #[test]
261 fn f64_stored_in_atomic_u64() {
262 fn prop(fs: Vec<f64>) {
263 let fs: Vec<f64> = fs
264 .into_iter()
265 .map(|f| if f.is_normal() { f } else { 0.0 })
267 .collect();
268 let sum = fs.iter().sum();
269 let counter = Counter::<f64, AtomicU64>::default();
270 for f in fs {
271 counter.inc_by(f);
272 }
273 assert_eq!(counter.get(), sum)
274 }
275
276 QuickCheck::new().tests(10).quickcheck(prop as fn(_))
277 }
278}