xref: /linux/rust/kernel/sync/atomic/predefine.rs (revision 7b001c97d9bdaea50e1e1834040c58f7ef9f4e89)
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