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::static_assert;
6use core::mem::{align_of, size_of};
7
8// Ensure size and alignment requirements are checked.
9static_assert!(size_of::<bool>() == size_of::<i8>());
10static_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`.
15unsafe 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.
21unsafe 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.
27unsafe 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.
33unsafe 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`.
38unsafe 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.
46unsafe 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`.
51unsafe 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))]
63type isize_atomic_repr = i32;
64#[allow(non_camel_case_types)]
65#[cfg(not(testlib))]
66#[cfg(CONFIG_64BIT)]
67type isize_atomic_repr = i64;
68
69#[allow(non_camel_case_types)]
70#[cfg(testlib)]
71#[cfg(target_pointer_width = "32")]
72type isize_atomic_repr = i32;
73#[allow(non_camel_case_types)]
74#[cfg(testlib)]
75#[cfg(target_pointer_width = "64")]
76type isize_atomic_repr = i64;
77
78// Ensure size and alignment requirements are checked.
79static_assert!(size_of::<isize>() == size_of::<isize_atomic_repr>());
80static_assert!(align_of::<isize>() == align_of::<isize_atomic_repr>());
81static_assert!(size_of::<usize>() == size_of::<isize_atomic_repr>());
82static_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`.
86unsafe 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`.
91unsafe 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`.
99unsafe 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`.
104unsafe 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`.
112unsafe 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`.
117unsafe 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`.
125unsafe 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`.
130unsafe 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
136use crate::macros::kunit_tests;
137
138#[kunit_tests(rust_atomics)]
139mod 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}