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