Skip to main content

kernel/sync/atomic/
predefine.rs

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