use std::sync::atomic::*;
use crate::raw::number::Number;
pub trait Atomic<N: Number>: Default + Send + Sync {
fn inc_by(&self, v: N);
fn dec_by(&self, v: N);
fn update<F>(&self, f: F)
where
F: FnMut(N) -> N;
fn set(&self, v: N);
fn get(&self) -> N;
}
macro_rules! impl_atomic_for_integer {
($($ty:ident => $atomic:ident);* $(;)?) => ($(
impl Atomic<$ty> for $atomic {
#[inline(always)]
fn inc_by(&self, v: $ty) {
self.fetch_add(v, Ordering::Relaxed);
}
#[inline(always)]
fn dec_by(&self, v: $ty) {
self.fetch_sub(v, Ordering::Relaxed);
}
#[inline]
fn update<F>(&self, mut f: F)
where
F: FnMut($ty) -> $ty,
{
let _ = self.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| Some(f(old)));
}
#[inline(always)]
fn set(&self, v: $ty) {
self.store(v, Ordering::Relaxed);
}
#[inline(always)]
fn get(&self) -> $ty {
self.load(Ordering::Relaxed)
}
}
)*);
}
impl_atomic_for_integer! {
i32 => AtomicI32;
i64 => AtomicI64;
isize => AtomicIsize;
u32 => AtomicU32;
u64 => AtomicU64;
usize => AtomicUsize;
}
macro_rules! impl_atomic_for_float {
($($ty:ident => $atomic:ident);* $(;)?) => ($(
impl Atomic<$ty> for $atomic {
#[inline(always)]
fn inc_by(&self, v: $ty) {
let _ = self.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old_bits| {
let old_value = $ty::from_bits(old_bits);
Some($ty::to_bits(old_value + v))
});
}
#[inline(always)]
fn dec_by(&self, v: $ty) {
let _ = self.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old_bits| {
let old_value = $ty::from_bits(old_bits);
Some($ty::to_bits(old_value - v))
});
}
#[inline]
fn update<F>(&self, mut f: F)
where
F: FnMut($ty) -> $ty,
{
let _ = self.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old_bits| {
let old_value = $ty::from_bits(old_bits);
let new_value = f(old_value);
Some($ty::to_bits(new_value))
});
}
#[inline]
fn set(&self, v: $ty) {
self.store($ty::to_bits(v), Ordering::Relaxed);
}
#[inline]
fn get(&self) -> $ty {
let value = self.load(Ordering::Relaxed);
$ty::from_bits(value)
}
}
)*);
}
impl_atomic_for_float! {
f32 => AtomicU32;
f64 => AtomicU64;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atomic_f32() {
let value = AtomicU32::new(0);
value.set(100f32);
let new: f32 = value.get();
assert_eq!(new, 100f32);
value.inc_by(10f32);
let new: f32 = value.get();
assert_eq!(new, 110f32);
value.dec_by(10f32);
let new: f32 = value.get();
assert_eq!(new, 100f32);
}
#[test]
fn test_atomic_f64() {
let value = AtomicU64::new(0);
value.set(100f64);
let new: f64 = value.get();
assert_eq!(new, 100f64);
value.inc_by(10f64);
let new: f64 = value.get();
assert_eq!(new, 110f64);
value.dec_by(10f64);
let new: f64 = value.get();
assert_eq!(new, 100f64);
}
#[test]
fn test_atomic_i32_update_saturating_overflow_underflow() {
let value = AtomicI32::new(0);
value.set(i32::MAX);
Atomic::update(&value, |old| old.saturating_add(1));
assert_eq!(value.get(), i32::MAX);
value.set(i32::MIN);
Atomic::update(&value, |old| old.saturating_sub(1));
assert_eq!(value.get(), i32::MIN);
}
#[test]
fn test_atomic_u64_update_saturating_overflow_underflow() {
let value = AtomicU64::new(0);
<AtomicU64 as Atomic<u64>>::set(&value, u64::MAX);
<AtomicU64 as Atomic<u64>>::update(&value, |old| old.saturating_add(1));
assert_eq!(<AtomicU64 as Atomic<u64>>::get(&value), u64::MAX);
<AtomicU64 as Atomic<u64>>::set(&value, 0);
<AtomicU64 as Atomic<u64>>::update(&value, |old| old.saturating_sub(1));
assert_eq!(<AtomicU64 as Atomic<u64>>::get(&value), 0);
}
}