prometheus/
atomic64.rs

1// Copyright 2014 The Prometheus Authors
2// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
3
4use std::cmp::*;
5use std::f64;
6use std::ops::*;
7use std::sync::atomic::{AtomicI64 as StdAtomicI64, AtomicU64 as StdAtomicU64, Ordering};
8
9/// An interface for numbers. Used to generically model float metrics and integer metrics, i.e.
10/// [`Counter`](crate::Counter) and [`IntCounter`](crate::Counter).
11pub trait Number:
12    Sized + AddAssign + SubAssign + PartialOrd + PartialEq + Copy + Send + Sync
13{
14    /// `std::convert::From<i64> for f64` is not implemented, so that we need to implement our own.
15    fn from_i64(v: i64) -> Self;
16    /// Convert to a f64.
17    fn into_f64(self) -> f64;
18}
19
20impl Number for i64 {
21    #[inline]
22    fn from_i64(v: i64) -> Self {
23        v
24    }
25
26    #[inline]
27    fn into_f64(self) -> f64 {
28        self as f64
29    }
30}
31
32impl Number for u64 {
33    #[inline]
34    fn from_i64(v: i64) -> Self {
35        v as u64
36    }
37
38    #[inline]
39    fn into_f64(self) -> f64 {
40        self as f64
41    }
42}
43
44impl Number for f64 {
45    #[inline]
46    fn from_i64(v: i64) -> Self {
47        v as f64
48    }
49
50    #[inline]
51    fn into_f64(self) -> f64 {
52        self
53    }
54}
55
56/// An interface for atomics. Used to generically model float metrics and integer metrics, i.e.
57/// [`Counter`](crate::Counter) and [`IntCounter`](crate::IntCounter).
58pub trait Atomic: Send + Sync {
59    /// The numeric type associated with this atomic.
60    type T: Number;
61    /// Create a new atomic value.
62    fn new(val: Self::T) -> Self;
63    /// Set the value to the provided value.
64    fn set(&self, val: Self::T);
65    /// Get the value.
66    fn get(&self) -> Self::T;
67    /// Increment the value by a given amount.
68    fn inc_by(&self, delta: Self::T);
69    /// Decrement the value by a given amount.
70    fn dec_by(&self, delta: Self::T);
71}
72
73/// A atomic float.
74#[derive(Debug)]
75pub struct AtomicF64 {
76    inner: StdAtomicU64,
77}
78
79#[inline]
80fn u64_to_f64(val: u64) -> f64 {
81    f64::from_bits(val)
82}
83
84#[inline]
85fn f64_to_u64(val: f64) -> u64 {
86    f64::to_bits(val)
87}
88
89impl Atomic for AtomicF64 {
90    type T = f64;
91
92    fn new(val: Self::T) -> AtomicF64 {
93        AtomicF64 {
94            inner: StdAtomicU64::new(f64_to_u64(val)),
95        }
96    }
97
98    #[inline]
99    fn set(&self, val: Self::T) {
100        self.inner.store(f64_to_u64(val), Ordering::Relaxed);
101    }
102
103    #[inline]
104    fn get(&self) -> Self::T {
105        u64_to_f64(self.inner.load(Ordering::Relaxed))
106    }
107
108    #[inline]
109    fn inc_by(&self, delta: Self::T) {
110        loop {
111            let current = self.inner.load(Ordering::Acquire);
112            let new = u64_to_f64(current) + delta;
113            let result = self.inner.compare_exchange_weak(
114                current,
115                f64_to_u64(new),
116                Ordering::Release,
117                Ordering::Relaxed,
118            );
119            if result.is_ok() {
120                return;
121            }
122        }
123    }
124
125    #[inline]
126    fn dec_by(&self, delta: Self::T) {
127        self.inc_by(-delta);
128    }
129}
130
131impl AtomicF64 {
132    /// Store the value, returning the previous value.
133    pub fn swap(&self, val: f64, ordering: Ordering) -> f64 {
134        u64_to_f64(self.inner.swap(f64_to_u64(val), ordering))
135    }
136}
137
138/// A atomic signed integer.
139#[derive(Debug)]
140pub struct AtomicI64 {
141    inner: StdAtomicI64,
142}
143
144impl Atomic for AtomicI64 {
145    type T = i64;
146
147    fn new(val: Self::T) -> AtomicI64 {
148        AtomicI64 {
149            inner: StdAtomicI64::new(val),
150        }
151    }
152
153    #[inline]
154    fn set(&self, val: Self::T) {
155        self.inner.store(val, Ordering::Relaxed);
156    }
157
158    #[inline]
159    fn get(&self) -> Self::T {
160        self.inner.load(Ordering::Relaxed)
161    }
162
163    #[inline]
164    fn inc_by(&self, delta: Self::T) {
165        self.inner.fetch_add(delta, Ordering::Relaxed);
166    }
167
168    #[inline]
169    fn dec_by(&self, delta: Self::T) {
170        self.inner.fetch_sub(delta, Ordering::Relaxed);
171    }
172}
173
174/// A atomic unsigned integer.
175#[derive(Debug)]
176pub struct AtomicU64 {
177    inner: StdAtomicU64,
178}
179
180impl Atomic for AtomicU64 {
181    type T = u64;
182
183    fn new(val: Self::T) -> AtomicU64 {
184        AtomicU64 {
185            inner: StdAtomicU64::new(val),
186        }
187    }
188
189    #[inline]
190    fn set(&self, val: Self::T) {
191        self.inner.store(val, Ordering::Relaxed);
192    }
193
194    #[inline]
195    fn get(&self) -> Self::T {
196        self.inner.load(Ordering::Relaxed)
197    }
198
199    #[inline]
200    fn inc_by(&self, delta: Self::T) {
201        self.inc_by_with_ordering(delta, Ordering::Relaxed);
202    }
203
204    #[inline]
205    fn dec_by(&self, delta: Self::T) {
206        self.inner.fetch_sub(delta, Ordering::Relaxed);
207    }
208}
209
210impl AtomicU64 {
211    /// Stores a value into the atomic integer if the current value is the same
212    /// as the current value.
213    ///
214    /// This function is allowed to spuriously fail even when the comparison
215    /// succeeds, which can result in more efficient code on some platforms. The
216    /// return value is a result indicating whether the new value was written
217    /// and containing the previous value.
218    ///
219    /// See [`StdAtomicU64`] for details.
220    pub(crate) fn compare_exchange_weak(
221        &self,
222        current: u64,
223        new: u64,
224        success: Ordering,
225        failure: Ordering,
226    ) -> Result<u64, u64> {
227        self.inner
228            .compare_exchange_weak(current, new, success, failure)
229    }
230
231    /// Increment the value by a given amount with the provided memory ordering.
232    pub fn inc_by_with_ordering(&self, delta: u64, ordering: Ordering) {
233        self.inner.fetch_add(delta, ordering);
234    }
235
236    /// Stores a value into the atomic integer, returning the previous value.
237    pub fn swap(&self, val: u64, ordering: Ordering) -> u64 {
238        self.inner.swap(val, ordering)
239    }
240}
241
242#[cfg(test)]
243mod test {
244    use std::f64::consts::PI;
245    use std::f64::{self, EPSILON};
246
247    use super::*;
248
249    #[test]
250    fn test_atomic_f64() {
251        let table: Vec<f64> = vec![0.0, 1.0, PI, f64::MIN, f64::MAX];
252
253        for f in table {
254            assert!((f - AtomicF64::new(f).get()).abs() < EPSILON);
255        }
256    }
257
258    #[test]
259    fn test_atomic_i64() {
260        let ai64 = AtomicI64::new(0);
261        assert_eq!(ai64.get(), 0);
262
263        ai64.inc_by(1);
264        assert_eq!(ai64.get(), 1);
265
266        ai64.inc_by(-5);
267        assert_eq!(ai64.get(), -4);
268    }
269
270    #[test]
271    fn test_atomic_u64() {
272        let au64 = AtomicU64::new(0);
273        assert_eq!(au64.get(), 0);
274
275        au64.inc_by(123);
276        assert_eq!(au64.get(), 123);
277    }
278}