1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Pre-defined atomic types 4 5 use crate::static_assert; 6 use core::mem::{align_of, size_of}; 7 8 // SAFETY: `i32` has the same size and alignment with itself, and is round-trip transmutable to 9 // itself. 10 unsafe impl super::AtomicType for i32 { 11 type Repr = i32; 12 } 13 14 // SAFETY: The wrapping add result of two `i32`s is a valid `i32`. 15 unsafe impl super::AtomicAdd<i32> for i32 { rhs_into_delta(rhs: i32) -> i3216 fn rhs_into_delta(rhs: i32) -> i32 { 17 rhs 18 } 19 } 20 21 // SAFETY: `i64` has the same size and alignment with itself, and is round-trip transmutable to 22 // itself. 23 unsafe impl super::AtomicType for i64 { 24 type Repr = i64; 25 } 26 27 // SAFETY: The wrapping add result of two `i64`s is a valid `i64`. 28 unsafe impl super::AtomicAdd<i64> for i64 { rhs_into_delta(rhs: i64) -> i6429 fn rhs_into_delta(rhs: i64) -> i64 { 30 rhs 31 } 32 } 33 34 // Defines an internal type that always maps to the integer type which has the same size alignment 35 // as `isize` and `usize`, and `isize` and `usize` are always bi-directional transmutable to 36 // `isize_atomic_repr`, which also always implements `AtomicImpl`. 37 #[allow(non_camel_case_types)] 38 #[cfg(not(testlib))] 39 #[cfg(not(CONFIG_64BIT))] 40 type isize_atomic_repr = i32; 41 #[allow(non_camel_case_types)] 42 #[cfg(not(testlib))] 43 #[cfg(CONFIG_64BIT)] 44 type isize_atomic_repr = i64; 45 46 #[allow(non_camel_case_types)] 47 #[cfg(testlib)] 48 #[cfg(target_pointer_width = "32")] 49 type isize_atomic_repr = i32; 50 #[allow(non_camel_case_types)] 51 #[cfg(testlib)] 52 #[cfg(target_pointer_width = "64")] 53 type isize_atomic_repr = i64; 54 55 // Ensure size and alignment requirements are checked. 56 static_assert!(size_of::<isize>() == size_of::<isize_atomic_repr>()); 57 static_assert!(align_of::<isize>() == align_of::<isize_atomic_repr>()); 58 static_assert!(size_of::<usize>() == size_of::<isize_atomic_repr>()); 59 static_assert!(align_of::<usize>() == align_of::<isize_atomic_repr>()); 60 61 // SAFETY: `isize` has the same size and alignment with `isize_atomic_repr`, and is round-trip 62 // transmutable to `isize_atomic_repr`. 63 unsafe impl super::AtomicType for isize { 64 type Repr = isize_atomic_repr; 65 } 66 67 // SAFETY: The wrapping add result of two `isize_atomic_repr`s is a valid `usize`. 68 unsafe impl super::AtomicAdd<isize> for isize { rhs_into_delta(rhs: isize) -> isize_atomic_repr69 fn rhs_into_delta(rhs: isize) -> isize_atomic_repr { 70 rhs as isize_atomic_repr 71 } 72 } 73 74 // SAFETY: `u32` and `i32` has the same size and alignment, and `u32` is round-trip transmutable to 75 // `i32`. 76 unsafe impl super::AtomicType for u32 { 77 type Repr = i32; 78 } 79 80 // SAFETY: The wrapping add result of two `i32`s is a valid `u32`. 81 unsafe impl super::AtomicAdd<u32> for u32 { rhs_into_delta(rhs: u32) -> i3282 fn rhs_into_delta(rhs: u32) -> i32 { 83 rhs as i32 84 } 85 } 86 87 // SAFETY: `u64` and `i64` has the same size and alignment, and `u64` is round-trip transmutable to 88 // `i64`. 89 unsafe impl super::AtomicType for u64 { 90 type Repr = i64; 91 } 92 93 // SAFETY: The wrapping add result of two `i64`s is a valid `u64`. 94 unsafe impl super::AtomicAdd<u64> for u64 { rhs_into_delta(rhs: u64) -> i6495 fn rhs_into_delta(rhs: u64) -> i64 { 96 rhs as i64 97 } 98 } 99 100 // SAFETY: `usize` has the same size and alignment with `isize_atomic_repr`, and is round-trip 101 // transmutable to `isize_atomic_repr`. 102 unsafe impl super::AtomicType for usize { 103 type Repr = isize_atomic_repr; 104 } 105 106 // SAFETY: The wrapping add result of two `isize_atomic_repr`s is a valid `usize`. 107 unsafe impl super::AtomicAdd<usize> for usize { rhs_into_delta(rhs: usize) -> isize_atomic_repr108 fn rhs_into_delta(rhs: usize) -> isize_atomic_repr { 109 rhs as isize_atomic_repr 110 } 111 } 112 113 use crate::macros::kunit_tests; 114 115 #[kunit_tests(rust_atomics)] 116 mod tests { 117 use super::super::*; 118 119 // Call $fn($val) with each $type of $val. 120 macro_rules! for_each_type { 121 ($val:literal in [$($type:ty),*] $fn:expr) => { 122 $({ 123 let v: $type = $val; 124 125 $fn(v); 126 })* 127 } 128 } 129 130 #[test] atomic_basic_tests()131 fn atomic_basic_tests() { 132 for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { 133 let x = Atomic::new(v); 134 135 assert_eq!(v, x.load(Relaxed)); 136 }); 137 } 138 139 #[test] atomic_xchg_tests()140 fn atomic_xchg_tests() { 141 for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { 142 let x = Atomic::new(v); 143 144 let old = v; 145 let new = v + 1; 146 147 assert_eq!(old, x.xchg(new, Full)); 148 assert_eq!(new, x.load(Relaxed)); 149 }); 150 } 151 152 #[test] atomic_cmpxchg_tests()153 fn atomic_cmpxchg_tests() { 154 for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { 155 let x = Atomic::new(v); 156 157 let old = v; 158 let new = v + 1; 159 160 assert_eq!(Err(old), x.cmpxchg(new, new, Full)); 161 assert_eq!(old, x.load(Relaxed)); 162 assert_eq!(Ok(old), x.cmpxchg(old, new, Relaxed)); 163 assert_eq!(new, x.load(Relaxed)); 164 }); 165 } 166 167 #[test] atomic_arithmetic_tests()168 fn atomic_arithmetic_tests() { 169 for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { 170 let x = Atomic::new(v); 171 172 assert_eq!(v, x.fetch_add(12, Full)); 173 assert_eq!(v + 12, x.load(Relaxed)); 174 175 x.add(13, Relaxed); 176 177 assert_eq!(v + 25, x.load(Relaxed)); 178 }); 179 } 180 } 181