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