fastmetrics/raw/
atomic.rs1use std::sync::atomic::*;
2
3use crate::raw::number::Number;
4
5pub trait Atomic<N: Number>: Default + Send + Sync {
17 fn inc_by(&self, v: N);
19
20 fn dec_by(&self, v: N);
22
23 fn update<F>(&self, f: F)
25 where
26 F: FnMut(N) -> N;
27
28 fn set(&self, v: N);
30
31 fn get(&self) -> N;
33}
34
35macro_rules! impl_atomic_for_integer {
36 ($($ty:ident => $atomic:ident);* $(;)?) => ($(
37 impl Atomic<$ty> for $atomic {
38 #[inline(always)]
39 fn inc_by(&self, v: $ty) {
40 self.fetch_add(v, Ordering::Relaxed);
41 }
42
43 #[inline(always)]
44 fn dec_by(&self, v: $ty) {
45 self.fetch_sub(v, Ordering::Relaxed);
46 }
47
48 #[inline]
49 fn update<F>(&self, mut f: F)
50 where
51 F: FnMut($ty) -> $ty,
52 {
53 let _ = self.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| Some(f(old)));
54 }
55
56 #[inline(always)]
57 fn set(&self, v: $ty) {
58 self.store(v, Ordering::Relaxed);
59 }
60
61 #[inline(always)]
62 fn get(&self) -> $ty {
63 self.load(Ordering::Relaxed)
64 }
65 }
66 )*);
67}
68
69impl_atomic_for_integer! {
70 i32 => AtomicI32;
71 i64 => AtomicI64;
72 isize => AtomicIsize;
73
74 u32 => AtomicU32;
75 u64 => AtomicU64;
76 usize => AtomicUsize;
77}
78
79macro_rules! impl_atomic_for_float {
80 ($($ty:ident => $atomic:ident);* $(;)?) => ($(
81 impl Atomic<$ty> for $atomic {
82 #[inline(always)]
83 fn inc_by(&self, v: $ty) {
84 let _ = self.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old_bits| {
85 let old_value = $ty::from_bits(old_bits);
86 Some($ty::to_bits(old_value + v))
87 });
88 }
89
90 #[inline(always)]
91 fn dec_by(&self, v: $ty) {
92 let _ = self.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old_bits| {
93 let old_value = $ty::from_bits(old_bits);
94 Some($ty::to_bits(old_value - v))
95 });
96 }
97
98 #[inline]
99 fn update<F>(&self, mut f: F)
100 where
101 F: FnMut($ty) -> $ty,
102 {
103 let _ = self.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old_bits| {
104 let old_value = $ty::from_bits(old_bits);
105 let new_value = f(old_value);
106 Some($ty::to_bits(new_value))
107 });
108 }
109
110 #[inline]
111 fn set(&self, v: $ty) {
112 self.store($ty::to_bits(v), Ordering::Relaxed);
113 }
114
115 #[inline]
116 fn get(&self) -> $ty {
117 let value = self.load(Ordering::Relaxed);
118 $ty::from_bits(value)
119 }
120 }
121 )*);
122}
123
124impl_atomic_for_float! {
125 f32 => AtomicU32;
126 f64 => AtomicU64;
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_atomic_f32() {
135 let value = AtomicU32::new(0);
136
137 value.set(100f32);
138 let new: f32 = value.get();
139 assert_eq!(new, 100f32);
140
141 value.inc_by(10f32);
142 let new: f32 = value.get();
143 assert_eq!(new, 110f32);
144
145 value.dec_by(10f32);
146 let new: f32 = value.get();
147 assert_eq!(new, 100f32);
148 }
149
150 #[test]
151 fn test_atomic_f64() {
152 let value = AtomicU64::new(0);
153
154 value.set(100f64);
155 let new: f64 = value.get();
156 assert_eq!(new, 100f64);
157
158 value.inc_by(10f64);
159 let new: f64 = value.get();
160 assert_eq!(new, 110f64);
161
162 value.dec_by(10f64);
163 let new: f64 = value.get();
164 assert_eq!(new, 100f64);
165 }
166
167 #[test]
168 fn test_atomic_i32_update_saturating_overflow_underflow() {
169 let value = AtomicI32::new(0);
170
171 value.set(i32::MAX);
172 Atomic::update(&value, |old| old.saturating_add(1));
173 assert_eq!(value.get(), i32::MAX);
174
175 value.set(i32::MIN);
176 Atomic::update(&value, |old| old.saturating_sub(1));
177 assert_eq!(value.get(), i32::MIN);
178 }
179
180 #[test]
181 fn test_atomic_u64_update_saturating_overflow_underflow() {
182 let value = AtomicU64::new(0);
183
184 <AtomicU64 as Atomic<u64>>::set(&value, u64::MAX);
185 <AtomicU64 as Atomic<u64>>::update(&value, |old| old.saturating_add(1));
186 assert_eq!(<AtomicU64 as Atomic<u64>>::get(&value), u64::MAX);
187
188 <AtomicU64 as Atomic<u64>>::set(&value, 0);
189 <AtomicU64 as Atomic<u64>>::update(&value, |old| old.saturating_sub(1));
190 assert_eq!(<AtomicU64 as Atomic<u64>>::get(&value), 0);
191 }
192}