// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause use std::sync::atomic::Ordering; /// # Safety /// /// Objects that implement this trait must consist exclusively of atomic types /// from [`std::sync::atomic`](https://doc.rust-lang.org/std/sync/atomic/), except for /// [`AtomicPtr`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicPtr.html) and /// [`AtomicBool`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html). pub unsafe trait AtomicInteger: Sync + Send { /// The raw value type associated with the atomic integer (i.e. `u16` for `AtomicU16`). type V; /// Create a new instance of `Self`. fn new(v: Self::V) -> Self; /// Loads a value from the atomic integer. fn load(&self, order: Ordering) -> Self::V; /// Stores a value into the atomic integer. fn store(&self, val: Self::V, order: Ordering); } macro_rules! impl_atomic_integer_ops { ($T:path, $V:ty) => { // SAFETY: This is safe as long as T is an Atomic type. // This is a helper macro for generating the implementation for common // Atomic types. unsafe impl AtomicInteger for $T { type V = $V; fn new(v: Self::V) -> Self { Self::new(v) } fn load(&self, order: Ordering) -> Self::V { self.load(order) } fn store(&self, val: Self::V, order: Ordering) { self.store(val, order) } } }; } // TODO: Detect availability using #[cfg(target_has_atomic) when it is stabilized. // Right now we essentially assume we're running on either x86 or Arm (32 or 64 bit). AFAIK, // Rust starts using additional synchronization primitives to implement atomics when they're // not natively available, and that doesn't interact safely with how we cast pointers to // atomic value references. We should be wary of this when looking at a broader range of // platforms. impl_atomic_integer_ops!(std::sync::atomic::AtomicI8, i8); impl_atomic_integer_ops!(std::sync::atomic::AtomicI16, i16); impl_atomic_integer_ops!(std::sync::atomic::AtomicI32, i32); #[cfg(any( target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64", target_arch = "s390x" ))] impl_atomic_integer_ops!(std::sync::atomic::AtomicI64, i64); impl_atomic_integer_ops!(std::sync::atomic::AtomicU8, u8); impl_atomic_integer_ops!(std::sync::atomic::AtomicU16, u16); impl_atomic_integer_ops!(std::sync::atomic::AtomicU32, u32); #[cfg(any( target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64", target_arch = "s390x" ))] impl_atomic_integer_ops!(std::sync::atomic::AtomicU64, u64); impl_atomic_integer_ops!(std::sync::atomic::AtomicIsize, isize); impl_atomic_integer_ops!(std::sync::atomic::AtomicUsize, usize); #[cfg(test)] mod tests { use super::*; use std::fmt::Debug; use std::sync::atomic::AtomicU32; fn check_atomic_integer_ops() where A::V: Copy + Debug + From + PartialEq, { let v = A::V::from(0); let a = A::new(v); assert_eq!(a.load(Ordering::Relaxed), v); let v2 = A::V::from(100); a.store(v2, Ordering::Relaxed); assert_eq!(a.load(Ordering::Relaxed), v2); } #[test] fn test_atomic_integer_ops() { check_atomic_integer_ops::() } }