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