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(CONFIG_64BIT))] 62 type isize_atomic_repr = i32; 63 #[allow(non_camel_case_types)] 64 #[cfg(CONFIG_64BIT)] 65 type isize_atomic_repr = i64; 66 67 // Ensure size and alignment requirements are checked. 68 static_assert!(size_of::<isize>() == size_of::<isize_atomic_repr>()); 69 static_assert!(align_of::<isize>() == align_of::<isize_atomic_repr>()); 70 static_assert!(size_of::<usize>() == size_of::<isize_atomic_repr>()); 71 static_assert!(align_of::<usize>() == align_of::<isize_atomic_repr>()); 72 73 // SAFETY: `isize` has the same size and alignment with `isize_atomic_repr`, and is round-trip 74 // transmutable to `isize_atomic_repr`. 75 unsafe impl super::AtomicType for isize { 76 type Repr = isize_atomic_repr; 77 } 78 79 // SAFETY: The wrapping add result of two `isize_atomic_repr`s is a valid `usize`. 80 unsafe impl super::AtomicAdd<isize> for isize { 81 fn rhs_into_delta(rhs: isize) -> isize_atomic_repr { 82 rhs as isize_atomic_repr 83 } 84 } 85 86 // SAFETY: `u32` and `i32` has the same size and alignment, and `u32` is round-trip transmutable to 87 // `i32`. 88 unsafe impl super::AtomicType for u32 { 89 type Repr = i32; 90 } 91 92 // SAFETY: The wrapping add result of two `i32`s is a valid `u32`. 93 unsafe impl super::AtomicAdd<u32> for u32 { 94 fn rhs_into_delta(rhs: u32) -> i32 { 95 rhs as i32 96 } 97 } 98 99 // SAFETY: `u64` and `i64` has the same size and alignment, and `u64` is round-trip transmutable to 100 // `i64`. 101 unsafe impl super::AtomicType for u64 { 102 type Repr = i64; 103 } 104 105 // SAFETY: The wrapping add result of two `i64`s is a valid `u64`. 106 unsafe impl super::AtomicAdd<u64> for u64 { 107 fn rhs_into_delta(rhs: u64) -> i64 { 108 rhs as i64 109 } 110 } 111 112 // SAFETY: `usize` has the same size and alignment with `isize_atomic_repr`, and is round-trip 113 // transmutable to `isize_atomic_repr`. 114 unsafe impl super::AtomicType for usize { 115 type Repr = isize_atomic_repr; 116 } 117 118 // SAFETY: The wrapping add result of two `isize_atomic_repr`s is a valid `usize`. 119 unsafe impl super::AtomicAdd<usize> for usize { 120 fn rhs_into_delta(rhs: usize) -> isize_atomic_repr { 121 rhs as isize_atomic_repr 122 } 123 } 124 125 use crate::macros::kunit_tests; 126 127 #[kunit_tests(rust_atomics)] 128 mod tests { 129 use super::super::*; 130 131 // Call $fn($val) with each $type of $val. 132 macro_rules! for_each_type { 133 ($val:literal in [$($type:ty),*] $fn:expr) => { 134 $({ 135 let v: $type = $val; 136 137 $fn(v); 138 })* 139 } 140 } 141 142 #[test] 143 fn atomic_basic_tests() { 144 for_each_type!(42 in [i8, i16, i32, i64, u32, u64, isize, usize] |v| { 145 let x = Atomic::new(v); 146 147 assert_eq!(v, x.load(Relaxed)); 148 }); 149 } 150 151 #[test] 152 fn atomic_acquire_release_tests() { 153 for_each_type!(42 in [i8, i16, i32, i64, u32, u64, isize, usize] |v| { 154 let x = Atomic::new(0); 155 156 x.store(v, Release); 157 assert_eq!(v, x.load(Acquire)); 158 }); 159 } 160 161 #[test] 162 fn atomic_xchg_tests() { 163 for_each_type!(42 in [i8, i16, i32, i64, u32, u64, isize, usize] |v| { 164 let x = Atomic::new(v); 165 166 let old = v; 167 let new = v + 1; 168 169 assert_eq!(old, x.xchg(new, Full)); 170 assert_eq!(new, x.load(Relaxed)); 171 }); 172 } 173 174 #[test] 175 fn atomic_cmpxchg_tests() { 176 for_each_type!(42 in [i8, i16, i32, i64, u32, u64, isize, usize] |v| { 177 let x = Atomic::new(v); 178 179 let old = v; 180 let new = v + 1; 181 182 assert_eq!(Err(old), x.cmpxchg(new, new, Full)); 183 assert_eq!(old, x.load(Relaxed)); 184 assert_eq!(Ok(old), x.cmpxchg(old, new, Relaxed)); 185 assert_eq!(new, x.load(Relaxed)); 186 }); 187 } 188 189 #[test] 190 fn atomic_arithmetic_tests() { 191 for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| { 192 let x = Atomic::new(v); 193 194 assert_eq!(v, x.fetch_add(12, Full)); 195 assert_eq!(v + 12, x.load(Relaxed)); 196 197 x.add(13, Relaxed); 198 199 assert_eq!(v + 25, x.load(Relaxed)); 200 }); 201 } 202 203 #[test] 204 fn atomic_bool_tests() { 205 let x = Atomic::new(false); 206 207 assert_eq!(false, x.load(Relaxed)); 208 x.store(true, Relaxed); 209 assert_eq!(true, x.load(Relaxed)); 210 211 assert_eq!(true, x.xchg(false, Relaxed)); 212 assert_eq!(false, x.load(Relaxed)); 213 214 assert_eq!(Err(false), x.cmpxchg(true, true, Relaxed)); 215 assert_eq!(false, x.load(Relaxed)); 216 assert_eq!(Ok(false), x.cmpxchg(false, true, Full)); 217 } 218 } 219