atomic_int/lib.rs
1/*
2 * Copyright 2023 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//! Crate features
39//! --------------
40//!
41//! Types that directly correspond with Rust’s standard atomics like
42//! [`AtomicU64`] are available with the feature `primitives` (enabled by
43//! default). This includes [`AtomicPtr`], even though it isn’t exactly an
44//! integer.
45//!
46//! Atomic C integer types like [`AtomicCInt`] and [`AtomicCUlong`] are
47//! available with the feature `c` (enabled by default). For more granularity,
48//! a separate feature exists for each C integer (e.g., `c_int` and `c_ulong`).
49//!
50//! The spinlock-based fallback implementation can cause deadlocks with signal
51//! handlers. To avoid this, enable the feature `signal`, which blocks incoming
52//! signals while the lock is held. This feature is Unix-specific.
53//!
54//! atomic-int can optionally depend on [`libc`]. If this dependency is
55//! enabled, atomic-int will use the C integer types from [`libc`] instead of
56//! [`core::ffi`]. This should not make a noticeable difference, but it can
57//! decrease the minimum required Rust version, as C integer types were added
58//! to [`core::ffi`] only in version 1.64. The feature `signal` always enables
59//! `libc`.
60//!
61//! This crate is `no_std` when `libc` is not enabled.
62//!
63//! [^1]: As long as the platform supports [`AtomicBool`], which is required
64//! for the fallback implementation.
65//!
66//! [`libc`]: https://docs.rs/libc/0.2
67//! [`c_int`]: ffi::c_int
68//! [`AtomicBool`]: atomic::AtomicBool
69
70#[allow(unused_imports)]
71use core::sync::atomic;
72
73#[allow(unused_imports)]
74#[cfg(not(feature = "libc"))]
75use core::ffi;
76
77#[allow(unused_imports)]
78#[cfg(feature = "libc")]
79use libc as ffi;
80
81mod detail {
82 pub trait HasAtomic {
83 type Atomic;
84 }
85}
86
87use detail::HasAtomic;
88
89macro_rules! with_primitive_atomics {
90 ($macro:path) => {
91 $macro!(AtomicI8, i8, target_has_atomic = "8");
92 $macro!(AtomicU8, u8, target_has_atomic = "8");
93 $macro!(AtomicI16, i16, target_has_atomic = "16");
94 $macro!(AtomicU16, u16, target_has_atomic = "16");
95 $macro!(AtomicI32, i32, target_has_atomic = "32");
96 $macro!(AtomicU32, u32, target_has_atomic = "32");
97 $macro!(AtomicI64, i64, target_has_atomic = "64");
98 $macro!(AtomicU64, u64, target_has_atomic = "64");
99 $macro!(AtomicI128, i128, any());
100 $macro!(AtomicU128, u128, any());
101 $macro!(AtomicIsize, isize, target_has_atomic = "ptr");
102 $macro!(AtomicUsize, usize, target_has_atomic = "ptr");
103 };
104}
105
106macro_rules! impl_has_atomic {
107 ($atomic:ident, $int:ident, $($cfg:tt)*) => {
108 #[cfg($($cfg)*)]
109 impl HasAtomic for $int {
110 type Atomic = atomic::$atomic;
111 }
112 };
113}
114
115with_primitive_atomics!(impl_has_atomic);
116
117#[allow(unused_macros)]
118macro_rules! define_primitive_atomic {
119 ($atomic:ident$(<$generic:ident>)?, $type:ty, $($cfg:tt)*) => {
120 #[cfg(all(not(doc), $($cfg)*))]
121 pub type $atomic$(<$generic>)? = atomic::$atomic$(<$generic>)?;
122
123 #[cfg(any(doc, not($($cfg)*)))]
124 #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "primitives")))]
125 /// An atomic
126 #[doc = concat!("[`", stringify!($type), "`].")]
127 ///
128 /// This is either an alias to the type in [`core::sync::atomic`], or,
129 /// if not available, a spinlock-based fallback type.
130 ///
131 /// [`*mut T`]: pointer
132 pub type $atomic$(<$generic>)? = fallback::$atomic$(<$generic>)?;
133 };
134}
135
136#[cfg(feature = "primitives")]
137with_primitive_atomics!(define_primitive_atomic);
138
139#[cfg(feature = "primitives")]
140define_primitive_atomic!(AtomicPtr<T>, *mut T, target_has_atomic = "ptr");
141
142#[cfg(feature = "primitives")]
143#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "primitives")))]
144/// An atomic [`bool`].
145///
146/// This type alias is provided for completeness, but it always points to the
147/// real [`AtomicBool`][real] in [`core::sync::atomic`], as even the fallback
148/// atomic implementation in this crate requires [`AtomicBool`][real].
149///
150/// [real]: atomic::AtomicBool
151pub type AtomicBool = atomic::AtomicBool;
152
153macro_rules! with_c_atomics {
154 ($macro:path) => {
155 #[cfg(feature = "c_char")]
156 $macro!(AtomicCChar, c_char, "c_char", has_c_char_atomic);
157 #[cfg(feature = "c_schar")]
158 $macro!(AtomicCSchar, c_schar, "c_schar", has_c_schar_atomic);
159 #[cfg(feature = "c_uchar")]
160 $macro!(AtomicCUchar, c_uchar, "c_uchar", has_c_uchar_atomic);
161 #[cfg(feature = "c_short")]
162 $macro!(AtomicCShort, c_short, "c_short", has_c_short_atomic);
163 #[cfg(feature = "c_ushort")]
164 $macro!(AtomicCUshort, c_ushort, "c_ushort", has_c_ushort_atomic);
165 #[cfg(feature = "c_int")]
166 $macro!(AtomicCInt, c_int, "c_int", has_c_int_atomic);
167 #[cfg(feature = "c_uint")]
168 $macro!(AtomicCUint, c_uint, "c_uint", has_c_uint_atomic);
169 #[cfg(feature = "c_long")]
170 $macro!(AtomicCLong, c_long, "c_long", has_c_long_atomic);
171 #[cfg(feature = "c_ulong")]
172 $macro!(AtomicCUlong, c_ulong, "c_ulong", has_c_ulong_atomic);
173 #[cfg(feature = "c_longlong")]
174 $macro!(
175 AtomicCLonglong,
176 c_longlong,
177 "c_longlong",
178 has_c_longlong_atomic
179 );
180 #[cfg(feature = "c_ulonglong")]
181 $macro!(
182 AtomicCUlonglong,
183 c_ulonglong,
184 "c_ulonglong",
185 has_c_ulonglong_atomic
186 );
187 };
188}
189
190#[allow(unused_macros)]
191macro_rules! define_c_atomic {
192 ($atomic:ident, $int:ident, $feature:literal, $cfg:ident) => {
193 #[cfg(all(not(doc), $cfg))]
194 pub type $atomic = <ffi::$int as HasAtomic>::Atomic;
195
196 #[cfg(any(doc, not($cfg)))]
197 #[cfg_attr(feature = "doc_cfg", doc(cfg(feature = $feature)))]
198 /// An atomic
199 #[doc = concat!("[`", stringify!($int), "`][1].")]
200 ///
201 /// This is either an alias to the appropriate atomic integer type in
202 /// [`core::sync::atomic`], or a spinlock-based fallback type.
203 #[doc = concat!("\n\n[1]: ffi::", stringify!($int))]
204 pub type $atomic = fallback::$atomic;
205 };
206}
207
208with_c_atomics!(define_c_atomic);
209
210mod fallback;
211
212#[rustfmt::skip]
213#[cfg(doc)]
214#[cfg_attr(feature = "doc_cfg", doc(cfg(doc)))]
215/// An example fallback implementation of an atomic integer.
216///
217/// When no built-in atomic for a certain integer type is available, its type
218/// alias in this crate points to a type like this, except with methods that
219/// take and return that integer type, rather than [`i32`].
220///
221/// This type internally uses spinlocks, which can cause deadlocks with signal
222/// handlers. To avoid this, enable the feature `signal`, which blocks incoming
223/// signals while the spinlock is held.
224///
225/// The API of this type is designed to be compatible with the atomic integer
226/// types in [`core::sync::atomic`].
227///
228/// This type is exposed only in the documentation for illustrative purposes.
229pub use fallback::AtomicFallback;
230
231#[rustfmt::skip]
232#[cfg(doc)]
233#[cfg_attr(feature = "doc_cfg", doc(cfg(doc)))]
234/// An example fallback implementation of an atomic pointer.
235///
236/// [`AtomicPtr`] points to a type like this when no built-in atomic pointer is
237/// available.
238///
239/// This type is the pointer version of [`AtomicFallback`]; see its
240/// documentation for more details. Like [`AtomicFallback`], this type is
241/// exposed only in the documentation for illustrative purposes.
242pub use fallback::AtomicFallbackPtr;