atomic_int/lib.rs
1/*
2 * Copyright 2023, 2025 taylor.fish <contact@taylor.fish>
3 *
4 * This file is part of atomic-int.
5 *
6 * atomic-int is licensed under the Apache License, Version 2.0
7 * (the "License"); you may not use atomic-int except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#![cfg_attr(not(feature = "libc"), no_std)]
20#![cfg_attr(feature = "doc_cfg", feature(doc_cfg))]
21#![deny(unsafe_op_in_unsafe_fn)]
22
23//! atomic-int provides atomics for additional integers, such as C/FFI types
24//! like [`c_int`].
25//!
26//! For integer types that are aliases of primitive integers that have built-in
27//! Rust atomics, this crate simply re-exports those atomics. Otherwise, this
28//! crate provides a spinlock-based fallback implementation with a compatible
29//! API.
30//!
31//! This crate also provides types that directly correspond with Rust’s
32//! standard atomics, like [`AtomicU64`], with the difference that the fallback
33//! implementation will similarly be used for any such atomics that are not
34//! supported on a given platform. Thus, all atomics provided by this crate are
35//! available on all platforms[^1] in some form—either the built-in or fallback
36//! implementation.
37//!
38//! [^1]: As long as the platform supports [`AtomicBool`], and compare-and-swap
39//! operations on [`AtomicBool`], which are required for the fallback
40//! implementation.
41//!
42//! Crate features
43//! --------------
44//!
45//! Types that directly correspond with Rust’s standard atomics like
46//! [`AtomicU64`] are available with the feature `primitives` (enabled by
47//! default). This includes [`AtomicPtr`], even though it isn’t exactly an
48//! integer.
49//!
50//! Atomic C integer types like [`AtomicCInt`] and [`AtomicCUlong`] are
51//! available with the feature `c` (enabled by default). For more granularity,
52//! a separate feature exists for each C integer (e.g., `c_int` and `c_ulong`).
53//!
54//! The spinlock-based fallback implementation can cause deadlocks with signal
55//! handlers. To avoid this, enable the feature `signal`, which blocks incoming
56//! signals while the lock is held. This feature is Unix-specific; on
57//! non-Unix-like operating systems it is a no-op.
58//!
59//! atomic-int can optionally depend on [`libc`]. If this dependency is
60//! enabled, atomic-int will use the C integer types from [`libc`] instead of
61//! [`core::ffi`]. This should not make a noticeable difference, but it can
62//! decrease the minimum required Rust version, as C integer types were added
63//! to [`core::ffi`] only in version 1.64. The feature `signal` always enables
64//! `libc`.
65//!
66//! For development purposes, the feature `force-fallback` is provided. This
67//! forces the fallback implementation to be used for all atomics, which can
68//! help you ensure your program doesn’t rely on functionality only provided by
69//! the native atomic types. It should not normally be enabled outside of
70//! testing.
71//!
72//! Use without `std`
73//! -----------------
74//!
75//! This crate is `no_std` when `libc` is not enabled.
76//!
77//! [`libc`]: https://docs.rs/libc/0.2
78//! [`c_int`]: ffi::c_int
79//! [`AtomicBool`]: atomic::AtomicBool
80
81#[allow(unused_imports)]
82use core::sync::atomic;
83
84#[cfg(not(feature = "libc"))]
85use core::ffi;
86
87#[cfg(feature = "libc")]
88use libc as ffi;
89
90#[allow(unused_imports)]
91use ffi as _;
92
93mod detail {
94 pub trait GetAtomicOrFallback {
95 type Type;
96 }
97
98 pub struct AtomicOrFallback<T, Fallback, const HAS_ATOMIC: bool>(
99 core::marker::PhantomData<fn() -> (T, Fallback)>,
100 );
101
102 impl<T, Fallback> GetAtomicOrFallback
103 for AtomicOrFallback<T, Fallback, false>
104 {
105 type Type = Fallback;
106 }
107}
108
109#[allow(dead_code)]
110use detail::{AtomicOrFallback, GetAtomicOrFallback};
111
112#[allow(dead_code)]
113struct AtomicMeta<T>(core::marker::PhantomData<fn() -> T>);
114
115#[allow(dead_code)]
116trait DefaultAtomicMeta {
117 const HAS_ATOMIC: bool = false;
118}
119
120impl<T> DefaultAtomicMeta for AtomicMeta<T> {}
121
122macro_rules! with_primitive_atomics {
123 ($macro:path) => {
124 $macro!(AtomicI8, i8, [target_has_atomic = "8"], "");
125 $macro!(AtomicU8, u8, [target_has_atomic = "8"], "");
126 $macro!(AtomicI16, i16, [target_has_atomic = "16"], "");
127 $macro!(AtomicU16, u16, [target_has_atomic = "16"], "");
128 $macro!(AtomicI32, i32, [target_has_atomic = "32"], "");
129 $macro!(AtomicU32, u32, [target_has_atomic = "32"], "");
130 $macro!(AtomicI64, i64, [target_has_atomic = "64"], "");
131 $macro!(AtomicU64, u64, [target_has_atomic = "64"], "");
132 $macro!(
133 AtomicI128,
134 i128,
135 [any()],
136 "**Note:** Because 128-bit atomics are unstable, this type is \
137 always a spinlock-based fallback. This may change in a future \
138 version of this crate."
139 );
140 $macro!(
141 AtomicU128,
142 u128,
143 [any()],
144 "**Note:** Because 128-bit atomics are unstable, this type is \
145 always a spinlock-based fallback. This may change in a future \
146 version of this crate."
147 );
148 $macro!(AtomicIsize, isize, [target_has_atomic = "ptr"], "");
149 $macro!(AtomicUsize, usize, [target_has_atomic = "ptr"], "");
150 };
151}
152
153macro_rules! impl_atomic_meta {
154 ($atomic:ident, $int:ident, [$($cfg:tt)*], $($x:tt)*) => {
155 #[cfg(all(not(feature = "force-fallback"), $($cfg)*))]
156 #[allow(dead_code)]
157 impl AtomicMeta<$int> {
158 pub const HAS_ATOMIC: bool = true;
159 }
160
161 #[cfg(all(not(feature = "force-fallback"), $($cfg)*))]
162 impl<Fallback> GetAtomicOrFallback
163 for AtomicOrFallback<$int, Fallback, true>
164 {
165 type Type = atomic::$atomic;
166 }
167 };
168}
169
170with_primitive_atomics!(impl_atomic_meta);
171
172#[allow(unused_macros)]
173macro_rules! define_primitive_atomic {
174 (
175 $atomic:ident$(<$generic:ident>)?,
176 $type:ty,
177 [$($cfg:tt)*],
178 $doc:expr
179 ) => {
180 #[cfg(all(not(doc), not(feature = "force-fallback"), $($cfg)*))]
181 pub type $atomic$(<$generic>)? = atomic::$atomic$(<$generic>)?;
182
183 #[cfg(any(doc, feature = "force-fallback", not($($cfg)*)))]
184 #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "primitives")))]
185 /// An atomic
186 #[doc = concat!("[`", stringify!($type), "`].")]
187 ///
188 /// This is either an alias to the type in [`core::sync::atomic`], or,
189 /// if not available[^1], a spinlock-based fallback type.
190 ///
191 /// [^1]: If an appropriate type exists in [`core::sync::atomic`], but
192 /// compare-and-swap operations are not provided for it (i.e., only
193 /// loads and stores are supported), it is still considered to be "not
194 /// available" for the purposes of this crate.
195 ///
196 #[doc = $doc]
197 pub type $atomic$(<$generic>)? = fallback::$atomic$(<$generic>)?;
198 };
199}
200
201#[cfg(feature = "primitives")]
202with_primitive_atomics!(define_primitive_atomic);
203
204#[cfg(feature = "primitives")]
205define_primitive_atomic!(
206 AtomicPtr<T>,
207 *mut T,
208 [target_has_atomic = "ptr"],
209 "[`*mut T`]: pointer"
210);
211
212#[cfg(feature = "primitives")]
213#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "primitives")))]
214/// An atomic [`bool`].
215///
216/// This type alias is provided for completeness, but it always points to the
217/// real [`AtomicBool`][real] in [`core::sync::atomic`], as even the fallback
218/// atomic implementation in this crate requires [`AtomicBool`][real].
219///
220/// [real]: atomic::AtomicBool
221pub type AtomicBool = atomic::AtomicBool;
222
223macro_rules! with_c_atomics {
224 ($macro:path) => {
225 #[cfg(feature = "c_char")]
226 $macro!(AtomicCChar, c_char, "c_char");
227 #[cfg(feature = "c_schar")]
228 $macro!(AtomicCSchar, c_schar, "c_schar");
229 #[cfg(feature = "c_uchar")]
230 $macro!(AtomicCUchar, c_uchar, "c_uchar");
231 #[cfg(feature = "c_short")]
232 $macro!(AtomicCShort, c_short, "c_short");
233 #[cfg(feature = "c_ushort")]
234 $macro!(AtomicCUshort, c_ushort, "c_ushort");
235 #[cfg(feature = "c_int")]
236 $macro!(AtomicCInt, c_int, "c_int");
237 #[cfg(feature = "c_uint")]
238 $macro!(AtomicCUint, c_uint, "c_uint");
239 #[cfg(feature = "c_long")]
240 $macro!(AtomicCLong, c_long, "c_long");
241 #[cfg(feature = "c_ulong")]
242 $macro!(AtomicCUlong, c_ulong, "c_ulong");
243 #[cfg(feature = "c_longlong")]
244 $macro!(AtomicCLonglong, c_longlong, "c_longlong");
245 #[cfg(feature = "c_ulonglong")]
246 $macro!(AtomicCUlonglong, c_ulonglong, "c_ulonglong");
247 };
248}
249
250#[allow(unused_macros)]
251macro_rules! alias_c_type {
252 ($atomic:ident, $int:ident, $($x:tt)*) => {
253 #[allow(clippy::incompatible_msrv)] // see note in Cargo.toml
254 #[allow(non_camel_case_types)]
255 pub type $int = super::ffi::$int;
256 };
257}
258
259mod c_types {
260 with_c_atomics!(alias_c_type);
261}
262
263#[allow(unused_macros)]
264macro_rules! define_c_atomic {
265 ($atomic:ident, $int:ident, $feature:literal) => {
266 #[cfg(not(doc))]
267 pub type $atomic = <AtomicOrFallback<
268 c_types::$int,
269 fallback::$atomic,
270 { AtomicMeta::<c_types::$int>::HAS_ATOMIC },
271 > as GetAtomicOrFallback>::Type;
272
273 #[cfg(doc)]
274 #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = $feature)))]
275 /// An atomic
276 #[doc = concat!("[`", stringify!($int), "`][1].")]
277 ///
278 /// This is either an alias to the appropriate atomic integer type in
279 /// [`core::sync::atomic`], or a spinlock-based fallback type.
280 ///
281 #[doc = concat!("[1]: ffi::", stringify!($int))]
282 pub type $atomic = fallback::$atomic;
283 };
284}
285
286with_c_atomics!(define_c_atomic);
287
288mod fallback;
289
290#[cfg(doc)]
291#[cfg_attr(feature = "doc_cfg", doc(cfg(doc)))]
292/// An example fallback implementation of an atomic integer.
293///
294/// When no built-in atomic for a certain integer type is available, its type
295/// alias in this crate points to a type like this, except with methods that
296/// take and return that integer type, rather than [`i32`].
297///
298/// This type internally uses spinlocks, which can cause deadlocks with signal
299/// handlers. To avoid this, enable the feature `signal`, which blocks incoming
300/// signals while the spinlock is held.
301///
302/// The API of this type is designed to be compatible with the atomic integer
303/// types in [`core::sync::atomic`].
304///
305/// This type is exposed only in the documentation for illustrative purposes.
306pub use fallback::AtomicFallback;
307
308#[cfg(doc)]
309#[cfg_attr(feature = "doc_cfg", doc(cfg(doc)))]
310/// An example fallback implementation of an atomic pointer.
311///
312/// [`AtomicPtr`] points to a type like this when no built-in atomic pointer is
313/// available.
314///
315/// This type is the pointer version of [`AtomicFallback`]; see its
316/// documentation for more details. Like [`AtomicFallback`], this type is
317/// exposed only in the documentation for illustrative purposes.
318pub use fallback::AtomicFallbackPtr;