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