augmented_atomics/
lib.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23
24//! This crate is part of <https://github.com/yamadapc/augmented-audio/>. It exposes a couple of
25//! extra very limited atomic data types which are useful for this repository.
26//!
27//! In particular, it exposes:
28//!
29//! * `AtomicF32`
30//! * `AtomicF64`
31//! * `AtomicEnum`
32//! * `AtomicOption`
33//!
34//! In addition, an `AtomicValue` trait is provided, which allows `AtomicOption` to be available.
35
36use std::sync::atomic::*;
37
38use num_traits::{FromPrimitive, ToPrimitive};
39
40pub use atomic_enum::AtomicEnum;
41pub use atomic_option::AtomicOption;
42
43mod atomic_enum;
44mod atomic_option;
45
46/// Trait of values that can be loaded / stored with relaxed operations.
47pub trait AtomicValue {
48    type Inner;
49
50    fn get(&self) -> Self::Inner;
51    fn set(&self, value: Self::Inner);
52}
53
54macro_rules! std_atomic_impl {
55    ($x: path, $inner: ident) => {
56        impl AtomicValue for $x {
57            type Inner = $inner;
58
59            #[inline]
60            fn get(&self) -> Self::Inner {
61                self.load(Ordering::Relaxed)
62            }
63
64            #[inline]
65            fn set(&self, value: Self::Inner) {
66                self.store(value, Ordering::Relaxed)
67            }
68        }
69    };
70}
71
72std_atomic_impl!(AtomicU8, u8);
73std_atomic_impl!(AtomicU16, u16);
74std_atomic_impl!(AtomicU32, u32);
75std_atomic_impl!(AtomicU64, u64);
76std_atomic_impl!(AtomicUsize, usize);
77std_atomic_impl!(AtomicI8, i8);
78std_atomic_impl!(AtomicI16, i16);
79std_atomic_impl!(AtomicI32, i32);
80std_atomic_impl!(AtomicI64, i64);
81std_atomic_impl!(AtomicBool, bool);
82
83pub trait AtomicFloatRepresentable: Sized {
84    type AtomicType: AtomicValue<Inner = Self> + From<Self>;
85}
86
87macro_rules! atomic_float {
88    ($name: ident, $backing: ident, $inner: ident) => {
89        /// Simple atomic floating point variable with relaxed ordering.
90        ///
91        /// Fork of atomic float from rust-vst.
92        #[cfg_attr(
93            not(target_arch = "wasm32"),
94            derive(serde_derive::Serialize, serde_derive::Deserialize)
95        )]
96        pub struct $name {
97            atomic: $backing,
98        }
99
100        impl $name {
101            /// New atomic float with initial value `value`.
102            #[inline]
103            pub fn new(value: $inner) -> Self {
104                Self {
105                    atomic: $backing::new(value.to_bits()),
106                }
107            }
108
109            /// Get the current value of the atomic float with relaxed ordering.
110            #[inline]
111            pub fn get(&self) -> $inner {
112                $inner::from_bits(self.atomic.load(Ordering::Relaxed))
113            }
114
115            /// Set the value of the atomic float to `value` with relaxed ordering.
116            #[inline]
117            pub fn set(&self, value: $inner) {
118                self.atomic.store(value.to_bits(), Ordering::Relaxed)
119            }
120
121            /// Get the current value of the atomic float with `ordering`.
122            #[inline]
123            pub fn load(&self, ordering: Ordering) -> $inner {
124                $inner::from_bits(self.atomic.load(ordering))
125            }
126
127            /// Set the value of the atomic float to `value` with `ordering`.
128            #[inline]
129            pub fn store(&self, value: $inner, ordering: Ordering) {
130                self.atomic.store(value.to_bits(), ordering)
131            }
132        }
133
134        impl Default for $name {
135            #[inline]
136            fn default() -> Self {
137                Self::new(0.0)
138            }
139        }
140
141        impl std::fmt::Debug for $name {
142            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143                std::fmt::Debug::fmt(&self.get(), f)
144            }
145        }
146
147        impl std::fmt::Display for $name {
148            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149                std::fmt::Display::fmt(&self.get(), f)
150            }
151        }
152
153        impl PartialEq for $name {
154            fn eq(&self, rhs: &Self) -> bool {
155                self.get() == rhs.get()
156            }
157        }
158
159        impl Clone for $name {
160            #[inline]
161            fn clone(&self) -> Self {
162                $name::from(self.get())
163            }
164        }
165
166        impl From<$inner> for $name {
167            #[inline]
168            fn from(value: $inner) -> Self {
169                $name::new(value)
170            }
171        }
172
173        impl From<$name> for $inner {
174            #[inline]
175            fn from(value: $name) -> Self {
176                value.get()
177            }
178        }
179
180        impl AtomicValue for $name {
181            type Inner = $inner;
182
183            #[inline]
184            fn get(&self) -> Self::Inner {
185                $name::get(self)
186            }
187
188            #[inline]
189            fn set(&self, value: Self::Inner) {
190                $name::set(self, value)
191            }
192        }
193
194        impl AtomicFloatRepresentable for $inner {
195            type AtomicType = $name;
196        }
197    };
198}
199
200atomic_float!(AtomicF32, AtomicU32, f32);
201atomic_float!(AtomicF64, AtomicU64, f64);
202
203impl<T: ToPrimitive + FromPrimitive> AtomicValue for AtomicEnum<T> {
204    type Inner = T;
205
206    #[inline]
207    fn get(&self) -> Self::Inner {
208        Self::get(self)
209    }
210
211    #[inline]
212    fn set(&self, value: Self::Inner) {
213        Self::set(self, value)
214    }
215}
216
217#[cfg(test)]
218mod test {
219    use super::*;
220
221    #[test]
222    fn test_f32_doesnt_lose_precision() {
223        let value = 9.977_324_5_f32;
224        let a_value = AtomicF32::new(value);
225        assert!((a_value.get() - value) < f32::EPSILON);
226    }
227}