xref: /linux/rust/kernel/sync/atomic/predefine.rs (revision 88b489385bfe3713497a63c0dcf4dd7852cf4568)
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: `i32` has the same size and alignment with itself, and is round-trip transmutable to
9 // itself.
10 unsafe impl super::AtomicType for i32 {
11     type Repr = i32;
12 }
13 
14 // SAFETY: The wrapping add result of two `i32`s is a valid `i32`.
15 unsafe impl super::AtomicAdd<i32> for i32 {
rhs_into_delta(rhs: i32) -> i3216     fn rhs_into_delta(rhs: i32) -> i32 {
17         rhs
18     }
19 }
20 
21 // SAFETY: `i64` has the same size and alignment with itself, and is round-trip transmutable to
22 // itself.
23 unsafe impl super::AtomicType for i64 {
24     type Repr = i64;
25 }
26 
27 // SAFETY: The wrapping add result of two `i64`s is a valid `i64`.
28 unsafe impl super::AtomicAdd<i64> for i64 {
rhs_into_delta(rhs: i64) -> i6429     fn rhs_into_delta(rhs: i64) -> i64 {
30         rhs
31     }
32 }
33 
34 // Defines an internal type that always maps to the integer type which has the same size alignment
35 // as `isize` and `usize`, and `isize` and `usize` are always bi-directional transmutable to
36 // `isize_atomic_repr`, which also always implements `AtomicImpl`.
37 #[allow(non_camel_case_types)]
38 #[cfg(not(CONFIG_64BIT))]
39 type isize_atomic_repr = i32;
40 #[allow(non_camel_case_types)]
41 #[cfg(CONFIG_64BIT)]
42 type isize_atomic_repr = i64;
43 
44 // Ensure size and alignment requirements are checked.
45 static_assert!(size_of::<isize>() == size_of::<isize_atomic_repr>());
46 static_assert!(align_of::<isize>() == align_of::<isize_atomic_repr>());
47 static_assert!(size_of::<usize>() == size_of::<isize_atomic_repr>());
48 static_assert!(align_of::<usize>() == align_of::<isize_atomic_repr>());
49 
50 // SAFETY: `isize` has the same size and alignment with `isize_atomic_repr`, and is round-trip
51 // transmutable to `isize_atomic_repr`.
52 unsafe impl super::AtomicType for isize {
53     type Repr = isize_atomic_repr;
54 }
55 
56 // SAFETY: The wrapping add result of two `isize_atomic_repr`s is a valid `usize`.
57 unsafe impl super::AtomicAdd<isize> for isize {
rhs_into_delta(rhs: isize) -> isize_atomic_repr58     fn rhs_into_delta(rhs: isize) -> isize_atomic_repr {
59         rhs as isize_atomic_repr
60     }
61 }
62 
63 // SAFETY: `u32` and `i32` has the same size and alignment, and `u32` is round-trip transmutable to
64 // `i32`.
65 unsafe impl super::AtomicType for u32 {
66     type Repr = i32;
67 }
68 
69 // SAFETY: The wrapping add result of two `i32`s is a valid `u32`.
70 unsafe impl super::AtomicAdd<u32> for u32 {
rhs_into_delta(rhs: u32) -> i3271     fn rhs_into_delta(rhs: u32) -> i32 {
72         rhs as i32
73     }
74 }
75 
76 // SAFETY: `u64` and `i64` has the same size and alignment, and `u64` is round-trip transmutable to
77 // `i64`.
78 unsafe impl super::AtomicType for u64 {
79     type Repr = i64;
80 }
81 
82 // SAFETY: The wrapping add result of two `i64`s is a valid `u64`.
83 unsafe impl super::AtomicAdd<u64> for u64 {
rhs_into_delta(rhs: u64) -> i6484     fn rhs_into_delta(rhs: u64) -> i64 {
85         rhs as i64
86     }
87 }
88 
89 // SAFETY: `usize` has the same size and alignment with `isize_atomic_repr`, and is round-trip
90 // transmutable to `isize_atomic_repr`.
91 unsafe impl super::AtomicType for usize {
92     type Repr = isize_atomic_repr;
93 }
94 
95 // SAFETY: The wrapping add result of two `isize_atomic_repr`s is a valid `usize`.
96 unsafe impl super::AtomicAdd<usize> for usize {
rhs_into_delta(rhs: usize) -> isize_atomic_repr97     fn rhs_into_delta(rhs: usize) -> isize_atomic_repr {
98         rhs as isize_atomic_repr
99     }
100 }
101 
102 use crate::macros::kunit_tests;
103 
104 #[kunit_tests(rust_atomics)]
105 mod tests {
106     use super::super::*;
107 
108     // Call $fn($val) with each $type of $val.
109     macro_rules! for_each_type {
110         ($val:literal in [$($type:ty),*] $fn:expr) => {
111             $({
112                 let v: $type = $val;
113 
114                 $fn(v);
115             })*
116         }
117     }
118 
119     #[test]
atomic_basic_tests()120     fn atomic_basic_tests() {
121         for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| {
122             let x = Atomic::new(v);
123 
124             assert_eq!(v, x.load(Relaxed));
125         });
126     }
127 
128     #[test]
atomic_xchg_tests()129     fn atomic_xchg_tests() {
130         for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| {
131             let x = Atomic::new(v);
132 
133             let old = v;
134             let new = v + 1;
135 
136             assert_eq!(old, x.xchg(new, Full));
137             assert_eq!(new, x.load(Relaxed));
138         });
139     }
140 
141     #[test]
atomic_cmpxchg_tests()142     fn atomic_cmpxchg_tests() {
143         for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| {
144             let x = Atomic::new(v);
145 
146             let old = v;
147             let new = v + 1;
148 
149             assert_eq!(Err(old), x.cmpxchg(new, new, Full));
150             assert_eq!(old, x.load(Relaxed));
151             assert_eq!(Ok(old), x.cmpxchg(old, new, Relaxed));
152             assert_eq!(new, x.load(Relaxed));
153         });
154     }
155 
156     #[test]
atomic_arithmetic_tests()157     fn atomic_arithmetic_tests() {
158         for_each_type!(42 in [i32, i64, u32, u64, isize, usize] |v| {
159             let x = Atomic::new(v);
160 
161             assert_eq!(v, x.fetch_add(12, Full));
162             assert_eq!(v + 12, x.load(Relaxed));
163 
164             x.add(13, Relaxed);
165 
166             assert_eq!(v + 25, x.load(Relaxed));
167         });
168     }
169 }
170