From b75a7f24a76283f42c47775dc52ab56c5e1c435f Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Fri, 24 Mar 2023 10:51:31 +0900 Subject: [PATCH 1/1] Import radium 0.7.0 --- .cargo_vcs_info.json | 5 + Cargo.lock | 16 + Cargo.toml | 29 ++ Cargo.toml.orig | 24 ++ LICENSE.txt | 21 + README.md | 80 ++++ build.rs | 118 ++++++ src/lib.rs | 914 +++++++++++++++++++++++++++++++++++++++++++ src/macros.rs | 178 +++++++++ src/types.rs | 78 ++++ 10 files changed, 1463 insertions(+) create mode 100644 .cargo_vcs_info.json create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Cargo.toml.orig create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 build.rs create mode 100644 src/lib.rs create mode 100644 src/macros.rs create mode 100644 src/types.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..389764d --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "3f27e0d827338aee919213fd071b99819a1b9fff" + } +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ae34cac --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "radium" +version = "0.7.0" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6bfe43d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "radium" +version = "0.7.0" +authors = ["Nika Layzell ", "myrrlyn "] +include = ["src/**/*.rs", "Cargo.toml", "README.md", "LICENSE.txt", "build.rs"] +description = "Portable interfaces for maybe-atomic types" +homepage = "https://github.com/bitvecto-rs/radium" +documentation = "https://docs.rs/radium" +readme = "README.md" +keywords = ["atomic", "cell", "sync", "generic", "trait"] +categories = ["concurrency", "no-std"] +license = "MIT" +repository = "https://github.com/bitvecto-rs/radium" + +[dependencies] +[dev-dependencies.static_assertions] +version = "1" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..8204e6d --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,24 @@ +[package] +name = "radium" +version = "0.7.0" +authors = [ + "Nika Layzell ", + "myrrlyn " +] +license = "MIT" +readme = "README.md" +repository = "https://github.com/bitvecto-rs/radium" +homepage = "https://github.com/bitvecto-rs/radium" +documentation = "https://docs.rs/radium" +description = "Portable interfaces for maybe-atomic types" +keywords = ["atomic", "cell", "sync", "generic", "trait"] +categories = ["concurrency", "no-std"] + +include = ["src/**/*.rs", "Cargo.toml", "README.md", "LICENSE.txt", "build.rs"] + +edition = "2018" + +[dependencies] + +[dev-dependencies] +static_assertions = "1" diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..504df28 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 kneecaw (Nika Layzell) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..81e3cf0 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# radium + +[![Latest Version][version_img]][crate_link] +[![Documentation][docs_img]][docs_link] + +`radium` provides abstractions and graceful degradation for behavior that *must* +be shared-mutable, but merely *may* use atomic instructions to do so. + +The primary export is the [`Radium`] trait. This is implemented on all symbols +in the [`atomic`] module, and on their [`Cell`] equivalents, and presents the +atomic inherent API as a trait. Your code can be generic over `Radium`, use a +stable and consistent API, and permit callers to select atomic or `Cell` +behavior as they need. + +The symbols in the [`atomic`] module are conditionally present according to the +target architecture’s atomic support. As such, code that is portable across +targets with varying atomic support cannot use those names directly. Instead, +the [`radium::types`] module provides names that will always exist, and forward +to the corresponding atomic type when it exists and the equivalent [`Cell`] +type when it does not. + +As the `cfg(target_has_atomic)` compiler attribute is unstable, `radium` +provides the macro `radium::if_atomic!` to perform conditional compilation based +on atomic availability. + +This crate is `#![no_std]`-compatible, and uses no non-core types. + +## Versioning + +Each change of supported target architecture will result in a new minor version. +Furthermore, `radium` is by definition attached to the Rust standard library. +As the atomic API evolves, `radium` will follow it. MSRV raising is always at +least a minor-version increase. + +If you require a backport of architecture discovery to older Rust versions, +please file an issue. We will happily backport upon request, but we do not +proactively guarantee support for compilers older than ~six months. + +## Target Architecture Compatibility + +Because the compiler does not expose this information to libraries, `radium` +uses a build script to detect the target architecture and emit its own +directives that mark the presence or absence of an atomic integer. We accomplish +this by reading the compiler’s target information records and copying the +information directly into the build script. + +If `radium` does not work for your architecture, please update the build script +to handle your target string and submit a pull request. We write the build +script on an as-needed basis; it is not proactively filled with all of the +information listed in the compiler. + +**NOTE**: The build script receives information through two variables: `TARGET` +and `CARGO_CFG_TARGET_ARCH`. The latter is equivalent to the value in +`cfg!(target_arch =)`; however, this value **does not** contain enough +information to fully disambiguate the target. The build script attempts to do +rudimentary parsing of the `env!(TARGET)` string; if this does not work for your +target, consider using the `TARGET_ARCH` matcher, or match on the full `TARGET` +string rather than the parse attempt. + +--- + +**@kneecaw** - +> Feelin' lazy: Has someone already written a helper trait abstracting +> operations over `AtomicUsize` and `Cell` for generic code which may +> not care about atomicity? + +**@ManishEarth** - +> no but call the crate radium +> +> (since people didn't care that it was radioactive and used it in everything) + + +[crate_link]: https://crates.io/crates/raidum "Crates.io package" +[docs_img]: https://docs.rs/radium/badge.svg "Radium documentation badge" +[docs_link]: https://docs.rs/radium "Radium documentation" +[version_img]: https://img.shields.io/crates/v/radium.svg "Radium version badge" +[`Cell`]: https://doc.rust-lang.org/core/cell/struct.Cell.html +[`Radium`]: https://docs.rs/radium/latest/radium/trait.Radium.html +[`atomic`]: https://doc.rust-lang.org/core/sync/atomic +[`radium::types`]: https://docs.rs/radium/latest/radium/types diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..884ec99 --- /dev/null +++ b/build.rs @@ -0,0 +1,118 @@ +//! Target detection +//! +//! This build script translates the target for which `radium` is being compiled +//! into a set of directives that the crate can use to control which atomic +//! symbols it attempts to name. +//! +//! The compiler maintains its store of target information here: +//! +//! +//! That module is not easily extracted into something that can be loaded here, +//! so we are replicating it through string matching on the target name until +//! we are able to uniquely identify targets through `cfg` checks. +//! +//! Use `rustc --print target-list` to enumerate the full list of targets +//! available, and `rustc --print cfg` (optionally with `-Z unstable-options`) +//! to see what `cfg` values are produced for a target. +//! +//! The missing `cfg` checks required for conditional compilation, rather than a +//! build script, are: +//! +//! - [`accessible`](https://github.com/rust-lang/rust/issues/64797) +//! - [`target_feature`](https://github.com/rust-lang/rust/issues/69098) +//! - [`target_has_atomic`](https://github.com/rust-lang/rust/issues/32976) +//! +//! Once any of these becomes usable on the stable series, we can switch to a +//! set of `cfg` checks instead of a build script. + +use std::{env, error::Error}; + +/// Collection of flags indicating whether the target processor supports atomic +/// instructions for a certain width. +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +struct Atomics { + /// Target supports 8-bit atomics + has_8: bool, + /// Target supports 16-bit atomics + has_16: bool, + /// Target supports 32-bit atomics + has_32: bool, + /// Target supports 64-bit atomics + has_64: bool, + /// Target supports word-width atomics + has_ptr: bool, +} + +impl Atomics { + const ALL: Self = Self { + has_8: true, + has_16: true, + has_32: true, + has_64: true, + has_ptr: true, + }; + const NONE: Self = Self { + has_8: false, + has_16: false, + has_32: false, + has_64: false, + has_ptr: false, + }; +} + +fn main() -> Result<(), Box> { + let mut atomics = Atomics::ALL; + + let target = env::var("TARGET")?; + // Add new target strings here with their atomic availability. + #[allow(clippy::match_single_binding, clippy::single_match)] + match &*target { + "arm-linux-androideabi" => atomics.has_64 = false, + _ => {} + } + + // If for some reason splitting fails, use the whole target string. + let tgt_arch = target.split("-").next().unwrap_or(&target); + // Additionally, the `cfg!(target_arch)` value may be of use for some + // targets. Note that it does **not** carry distinguishing information in + // all cases! `armv5te` and `armv7` targets are both + // `cfg!(target_arch = "arm")`. + let env_arch = env::var("CARGO_CFG_TARGET_ARCH")?; + // Add new architecture sections here with their atomic availability. + #[allow(clippy::match_single_binding, clippy::single_match)] + match tgt_arch { + "armv5te" | "mips" | "mipsel" | "powerpc" | "riscv32imac" | "thumbv7em" | "thumbv7m" + | "thumbv8m.base" | "thumbv8m.main" | "armebv7r" | "armv7r" => atomics.has_64 = false, + // These ARMv7 targets have 32-bit pointers and 64-bit atomics. + "armv7" | "armv7a" | "armv7s" => atomics.has_64 = true, + // "riscv32imc-unknown-none-elf" and "riscv32imac-unknown-none-elf" are + // both `target_arch = "riscv32", and have no stable `cfg`-discoverable + // distinction. As such, the non-atomic RISC-V targets must be + // discovered here. + "riscv32i" | "riscv32imc" | "thumbv6m" => atomics = Atomics::NONE, + _ => {} + } + #[allow(clippy::match_single_binding, clippy::single_match)] + match &*env_arch { + "avr" => atomics = Atomics::NONE, + _ => {} + } + + if atomics.has_8 { + println!("cargo:rustc-cfg=radium_atomic_8"); + } + if atomics.has_16 { + println!("cargo:rustc-cfg=radium_atomic_16"); + } + if atomics.has_32 { + println!("cargo:rustc-cfg=radium_atomic_32"); + } + if atomics.has_64 { + println!("cargo:rustc-cfg=radium_atomic_64"); + } + if atomics.has_ptr { + println!("cargo:rustc-cfg=radium_atomic_ptr"); + } + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..00e9588 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,914 @@ +//! `radium` provides a series of helpers for a uniform API over both atomic +//! types like [`AtomicUsize`], and non-atomic types like [`Cell`]. +//! +//! This crate is `#![no_std]`-compatible, and uses no non-core types. +//! +//! For details, see the documentation for [`Radium`]. +//! +//! The `types` module provides type names that are atomic where the target +//! supports it, and fall back to `Cell` when the target does not. +//! +//! The `if_atomic!` macro provides a means of conditional compilation based on +//! the presence of atomic instructions. It is a substitute for the +//! `cfg(target_has_atomic)` or `cfg(accessible)` attribute tests, which are not +//! yet stabilized. +//! +//! --- +//! +//! **@kneecaw** - +//! > Feelin' lazy: Has someone already written a helper trait abstracting +//! > operations over `AtomicUsize` and `Cell` for generic code which may +//! > not care about atomicity? +//! +//! **@ManishEarth** - +//! > no but call the crate radium +//! > +//! > (since people didn't care that it was radioactive and used it in everything) +//! +//! [`AtomicUsize`]: core::sync::atomic::AtomicUsize +//! [`Cell`]: core::cell::Cell + +#![no_std] +#![deny(unconditional_recursion)] + +#[macro_use] +mod macros; + +pub mod types; + +use core::cell::Cell; +use core::sync::atomic::Ordering; + +if_atomic! { + if atomic(8) { + use core::sync::atomic::{AtomicBool, AtomicI8, AtomicU8}; + } + if atomic(16) { + use core::sync::atomic::{AtomicI16, AtomicU16}; + } + if atomic(32) { + use core::sync::atomic::{AtomicI32, AtomicU32}; + } + if atomic(64) { + use core::sync::atomic::{AtomicI64, AtomicU64}; + } + if atomic(ptr) { + use core::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize}; + } +} + +/// A maybe-atomic shared mutable fundamental type `T`. +/// +/// This trait is implemented by both the [atomic wrapper] type for `T`, and by +/// [`Cell`], providing a consistent interface for interacting with the two +/// types. +/// +/// This trait provides methods predicated on marker traits for the underlying +/// fundamental. Only types which can be viewed as sequences of bits may use the +/// functions for bit-wise arithmetic, and only types which can be used as +/// integers may use the functions for numeric arithmetic. Use of these methods +/// on insufficient underlying types (for example, `Radium::fetch_and` on an +/// atomic or cell-wrapped pointer) will cause a compiler error. +/// +/// [atomic wrapper]: core::sync::atomic +/// [`Cell`]: core::cell::Cell +pub trait Radium { + type Item; + /// Creates a new value of this type. + fn new(value: Self::Item) -> Self; + + /// If the underlying value is atomic, calls [`fence`] with the given + /// [`Ordering`]. Otherwise, does nothing. + /// + /// [`Ordering`]: core::sync::atomic::Ordering + /// [`fence`]: core::sync::atomic::fence + fn fence(order: Ordering); + + /// Returns a mutable reference to the underlying value. + /// + /// This is safe because the mutable reference to `self` guarantees that no + /// other references exist to this value. + fn get_mut(&mut self) -> &mut Self::Item; + + /// Consumes the wrapper and returns the contained value. + /// + /// This is safe as passing by value ensures no other references exist. + fn into_inner(self) -> Self::Item; + + /// Load a value from this object. + /// + /// Ordering values are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::load`]. + /// + /// [`AtomicUsize::load`]: core::sync::atomic::AtomicUsize::load + fn load(&self, order: Ordering) -> Self::Item; + + /// Store a value in this object. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::store`]. + /// + /// [`AtomicUsize::store`]: core::sync::atomic::AtomicUsize::store + fn store(&self, value: Self::Item, order: Ordering); + + /// Swap with the value stored in this object. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::swap`]. + /// + /// [`AtomicUsize::swap`]: core::sync::atomic::AtomicUsize::swap + fn swap(&self, value: Self::Item, order: Ordering) -> Self::Item; + + /// Stores a value into this object if the currently-stored value is the + /// same as the `current` value. + /// + /// The return value is always the previously-stored value. If it is equal to + /// `current`, then the value was updated with `new`. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::compare_and_swap`]. + /// + /// [`AtomicUsize::compare_and_swap`]: core::sync::atomic::AtomicUsize::compare_and_swap + #[deprecated = "Use `compare_exchange` or `compare_exchange_weak` instead"] + fn compare_and_swap(&self, current: Self::Item, new: Self::Item, order: Ordering) + -> Self::Item; + + /// Stores a value into this object if the currently-stored value is the + /// same as the `current` value. + /// + /// The return value is a `Result` indicating whether the new value was + /// written, and containing the previously-stored value. On success, this + /// value is guaranteed to be equal to `current`. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::compare_exchange`]. + /// + /// [`AtomicUsize::compare_exchange`]: core::sync::atomic::AtomicUsize::compare_exchange + fn compare_exchange( + &self, + current: Self::Item, + new: Self::Item, + success: Ordering, + failure: Ordering, + ) -> Result; + + /// Stores a value into this object if the currently-stored value is the + /// same as the `current` value. + /// + /// Unlike `compare_exchange`, this function is allowed to spuriously fail + /// even when the comparison succeeds, which can result in more efficient + /// code on some platforms. The return value is a `Result` indicating + /// whether the new value was written, and containing the previously-stored + /// value. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::compare_exchange_weak`]. + /// + /// [`AtomicUsize::compare_exchange_weak`]: core::sync::atomic::AtomicUsize::compare_exchange_weak + fn compare_exchange_weak( + &self, + current: Self::Item, + new: Self::Item, + success: Ordering, + failure: Ordering, + ) -> Result; + + /// Performs a bitwise "and" on the currently-stored value and the argument + /// `value`, and stores the result in `self`. + /// + /// Returns the previously-stored value. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::fetch_and`]. + /// + /// [`AtomicUsize::fetch_and`]: core::sync::atomic::AtomicUsize::fetch_and + fn fetch_and(&self, value: Self::Item, order: Ordering) -> Self::Item + where + Self::Item: marker::BitOps; + + /// Performs a bitwise "nand" on the currently-stored value and the argument + /// `value`, and stores the result in `self`. + /// + /// Returns the previously-stored value. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::fetch_nand`]. + /// + /// [`AtomicUsize::fetch_nand`]: core::sync::atomic::AtomicUsize::fetch_nand + fn fetch_nand(&self, value: Self::Item, order: Ordering) -> Self::Item + where + Self::Item: marker::BitOps; + + /// Performs a bitwise "or" on the currently-stored value and the argument + /// `value`, and stores the result in `self`. + /// + /// Returns the previously-stored value. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::fetch_or`]. + /// + /// [`AtomicUsize::fetch_or`]: core::sync::atomic::AtomicUsize::fetch_or + fn fetch_or(&self, value: Self::Item, order: Ordering) -> Self::Item + where + Self::Item: marker::BitOps; + + /// Performs a bitwise "xor" on the currently-stored value and the argument + /// `value`, and stores the result in `self`. + /// + /// Returns the previously-stored value. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::fetch_xor`]. + /// + /// [`AtomicUsize::fetch_xor`]: core::sync::atomic::AtomicUsize::fetch_xor + fn fetch_xor(&self, value: Self::Item, order: Ordering) -> Self::Item + where + Self::Item: marker::BitOps; + + /// Adds `value` to the currently-stored value, wrapping on overflow, and + /// stores the result in `self`. + /// + /// Returns the previously-stored value. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::fetch_add`]. + /// + /// [`AtomicUsize::fetch_add`]: core::sync::atomic::AtomicUsize::fetch_add + fn fetch_add(&self, value: Self::Item, order: Ordering) -> Self::Item + where + Self::Item: marker::NumericOps; + + /// Subtracts `value` from the currently-stored value, wrapping on + /// underflow, and stores the result in `self`. + /// + /// Returns the previously-stored value. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::fetch_sub`]. + /// + /// [`AtomicUsize::fetch_sub`]: core::sync::atomic::AtomicUsize::fetch_sub + fn fetch_sub(&self, value: Self::Item, order: Ordering) -> Self::Item + where + Self::Item: marker::NumericOps; + + /// Fetches the value, and applies a function to it that returns an + /// optional new value. + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// Returns a `Result` of `Ok(previous_value)` if the function returned + /// `Some(_)`, else `Err(previous_value)`. + /// + /// Ordering arguments are ignored by non-atomic types. + /// + /// See also: [`AtomicUsize::fetch_update`]. + /// + /// [`AtomicUsize::fetch_update`]: core::sync::atomic::AtomicUsize::fetch_update + fn fetch_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: F, + ) -> Result + where + F: FnMut(Self::Item) -> Option; +} + +/// Marker traits used by [`Radium`]. +pub mod marker { + /// Types supporting maybe-atomic bitwise operations. + /// + /// Types implementing this trait support the [`fetch_and`], [`fetch_nand`], + /// [`fetch_or`], and [`fetch_xor`] maybe-atomic operations. + /// + /// [`fetch_and`]: crate::Radium::fetch_and + /// [`fetch_nand`]: crate::Radium::fetch_nand + /// [`fetch_or`]: crate::Radium::fetch_or + /// [`fetch_xor`]: crate::Radium::fetch_xor + /// + /// `bool` and all integer fundamental types implement this. + /// + /// ```rust + /// # use core::sync::atomic::*; + /// # use radium::Radium; + /// let num: AtomicUsize = AtomicUsize::new(0); + /// Radium::fetch_or(&num, 2, Ordering::Relaxed); + /// ``` + /// + /// Pointers do not. This will cause a compiler error. + /// + /// ```rust,compile_fail + /// # use core::sync::atomic::*; + /// # use radium::Radium; + /// # use core::ptr; + /// let ptr: AtomicPtr = Default::default(); + /// Radium::fetch_or(&ptr, ptr::null_mut(), Ordering::Relaxed); + /// ``` + pub trait BitOps {} + + /// Types supporting maybe-atomic arithmetic operations. + /// + /// Types implementing this trait support the [`fetch_add`] and + /// [`fetch_sub`] maybe-atomic operations. + /// + /// [`fetch_add`]: crate::Radium::fetch_add + /// [`fetch_sub`]: crate::Radium::fetch_sub + /// + /// The integer types, such as `usize` and `i32`, implement this trait. + /// + /// ```rust + /// # use core::sync::atomic::*; + /// # use radium::Radium; + /// let num: AtomicUsize = AtomicUsize::new(2); + /// Radium::fetch_add(&num, 2, Ordering::Relaxed); + /// ``` + /// + /// `bool` and pointers do not. This will cause a compiler error. + /// + /// ```rust,compile_fail + /// # use core::sync::atomic::*; + /// # use radium::Radium; + /// let bit: AtomicBool = AtomicBool::new(false); + /// Radium::fetch_add(&bit, true, Ordering::Relaxed); + /// ``` + pub trait NumericOps: BitOps {} +} + +macro_rules! radium { + // Emit the universal `Radium` trait function bodies for atomic types. + ( atom $base:ty ) => { + #[inline] + fn new(value: $base) -> Self { + Self::new(value) + } + + #[inline] + fn fence(order: Ordering) { + core::sync::atomic::fence(order); + } + + #[inline] + fn get_mut(&mut self) -> &mut $base { + self.get_mut() + } + + #[inline] + fn into_inner(self) -> $base { + self.into_inner() + } + + #[inline] + fn load(&self, order: Ordering) -> $base { + self.load(order) + } + + #[inline] + fn store(&self, value: $base, order: Ordering) { + self.store(value, order); + } + + #[inline] + fn swap(&self, value: $base, order: Ordering) -> $base { + self.swap(value, order) + } + + #[inline] + #[allow(deprecated)] + fn compare_and_swap(&self, current: $base, new: $base, order: Ordering) -> $base { + self.compare_and_swap(current, new, order) + } + + #[inline] + fn compare_exchange( + &self, + current: $base, + new: $base, + success: Ordering, + failure: Ordering, + ) -> Result<$base, $base> { + self.compare_exchange(current, new, success, failure) + } + + #[inline] + fn compare_exchange_weak( + &self, + current: $base, + new: $base, + success: Ordering, + failure: Ordering, + ) -> Result<$base, $base> { + self.compare_exchange_weak(current, new, success, failure) + } + + #[inline] + fn fetch_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: F, + ) -> Result<$base, $base> + where + F: FnMut($base) -> Option<$base>, + { + self.fetch_update(set_order, fetch_order, f) + } + }; + + // Emit the `Radium` trait function bodies for bit-wise types. + ( atom_bit $base:ty ) => { + #[inline] + fn fetch_and(&self, value: $base, order: Ordering) -> $base { + self.fetch_and(value, order) + } + + #[inline] + fn fetch_nand(&self, value: $base, order: Ordering) -> $base { + self.fetch_nand(value, order) + } + + #[inline] + fn fetch_or(&self, value: $base, order: Ordering) -> $base { + self.fetch_or(value, order) + } + + #[inline] + fn fetch_xor(&self, value: $base, order: Ordering) -> $base { + self.fetch_xor(value, order) + } + }; + + // Emit the `Radium` trait function bodies for integral types. + ( atom_int $base:ty ) => { + #[inline] + fn fetch_add(&self, value: $base, order: Ordering) -> $base { + self.fetch_add(value, order) + } + + #[inline] + fn fetch_sub(&self, value: $base, order: Ordering) -> $base { + self.fetch_sub(value, order) + } + }; + + // Emit the universal `Radium` trait function bodies for `Cell<_>`. + ( cell $base:ty ) => { + #[inline] + fn new(value: $base) -> Self { + Cell::new(value) + } + + #[inline] + fn fence(_: Ordering) {} + + #[inline] + fn get_mut(&mut self) -> &mut $base { + self.get_mut() + } + + #[inline] + fn into_inner(self) -> $base { + self.into_inner() + } + + #[inline] + fn load(&self, _: Ordering) -> $base { + self.get() + } + + #[inline] + fn store(&self, value: $base, _: Ordering) { + self.set(value); + } + + #[inline] + fn swap(&self, value: $base, _: Ordering) -> $base { + self.replace(value) + } + + #[inline] + fn compare_and_swap(&self, current: $base, new: $base, _: Ordering) -> $base { + if self.get() == current { + self.replace(new) + } else { + self.get() + } + } + + #[inline] + fn compare_exchange( + &self, + current: $base, + new: $base, + _: Ordering, + _: Ordering, + ) -> Result<$base, $base> { + if self.get() == current { + Ok(self.replace(new)) + } else { + Err(self.get()) + } + } + + #[inline] + fn compare_exchange_weak( + &self, + current: $base, + new: $base, + success: Ordering, + failure: Ordering, + ) -> Result<$base, $base> { + Radium::compare_exchange(self, current, new, success, failure) + } + + #[inline] + fn fetch_update(&self, _: Ordering, _: Ordering, mut f: F) -> Result<$base, $base> + where + F: FnMut($base) -> Option<$base>, + { + match f(self.get()) { + Some(x) => Ok(self.replace(x)), + None => Err(self.get()), + } + } + }; + + // Emit the `Radium` trait function bodies for bit-wise types. + ( cell_bit $base:ty ) => { + #[inline] + fn fetch_and(&self, value: $base, _: Ordering) -> $base { + self.replace(self.get() & value) + } + + #[inline] + fn fetch_nand(&self, value: $base, _: Ordering) -> $base { + self.replace(!(self.get() & value)) + } + + #[inline] + fn fetch_or(&self, value: $base, _: Ordering) -> $base { + self.replace(self.get() | value) + } + + #[inline] + fn fetch_xor(&self, value: $base, _: Ordering) -> $base { + self.replace(self.get() ^ value) + } + }; + + // Emit the `Radium` trait function bodies for integral types. + ( cell_int $base:ty ) => { + #[inline] + fn fetch_add(&self, value: $base, _: Ordering) -> $base { + self.replace(self.get().wrapping_add(value)) + } + + #[inline] + fn fetch_sub(&self, value: $base, _: Ordering) -> $base { + self.replace(self.get().wrapping_sub(value)) + } + }; +} + +macro_rules! radium_int { + ( $( $width:tt: $base:ty , $atom:ty ; )* ) => { $( + impl marker::BitOps for $base {} + impl marker::NumericOps for $base {} + + if_atomic!(if atomic($width) { + impl Radium for $atom { + type Item = $base; + + radium!(atom $base); + radium!(atom_bit $base); + radium!(atom_int $base); + } + }); + + impl Radium for Cell<$base> { + type Item = $base; + + radium!(cell $base); + radium!(cell_bit $base); + radium!(cell_int $base); + } + )* }; +} + +radium_int! { + 8: i8, AtomicI8; + 8: u8, AtomicU8; + 16: i16, AtomicI16; + 16: u16, AtomicU16; + 32: i32, AtomicI32; + 32: u32, AtomicU32; + 64: i64, AtomicI64; + 64: u64, AtomicU64; + size: isize, AtomicIsize; + size: usize, AtomicUsize; +} + +impl marker::BitOps for bool {} + +if_atomic!(if atomic(bool) { + impl Radium for AtomicBool { + type Item = bool; + + radium!(atom bool); + radium!(atom_bit bool); + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let atom = AtomicBool::new(false); + /// Radium::fetch_add(&atom, true, Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_add(&self, _value: bool, _order: Ordering) -> bool { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let atom = AtomicBool::new(false); + /// Radium::fetch_sub(&atom, true, Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_sub(&self, _value: bool, _order: Ordering) -> bool { + unreachable!("This method statically cannot be called") + } + } +}); + +impl Radium for Cell { + type Item = bool; + + radium!(cell bool); + radium!(cell_bit bool); + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let cell = Cell::::new(false); + /// Radium::fetch_add(&cell, true, Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_add(&self, _value: bool, _order: Ordering) -> bool { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let cell = Cell::::new(false); + /// Radium::fetch_sub(&cell, true, Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_sub(&self, _value: bool, _order: Ordering) -> bool { + unreachable!("This method statically cannot be called") + } +} + +if_atomic!(if atomic(ptr) { + impl Radium for AtomicPtr { + type Item = *mut T; + + radium!(atom *mut T); + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let atom = AtomicPtr::::new(ptr::null_mut()); + /// Radium::fetch_and(&atom, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_and(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let atom = AtomicPtr::::new(ptr::null_mut()); + /// Radium::fetch_nand(&atom, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_nand(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let atom = AtomicPtr::::new(ptr::null_mut()); + /// Radium::fetch_or(&atom, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_or(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let atom = AtomicPtr::::new(ptr::null_mut()); + /// Radium::fetch_xor(&atom, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_xor(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let atom = AtomicPtr::::new(ptr::null_mut()); + /// Radium::fetch_add(&atom, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_add(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let atom = AtomicPtr::::new(ptr::null_mut()); + /// Radium::fetch_sub(&atom, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_sub(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + } +}); + +impl Radium for Cell<*mut T> { + type Item = *mut T; + + radium!(cell *mut T); + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let cell = Cell::<*mut u8>::new(ptr::null_mut()); + /// Radium::fetch_and(&cell, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_and(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let cell = Cell::<*mut u8>::new(ptr::null_mut()); + /// Radium::fetch_nand(&cell, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_nand(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let cell = Cell::<*mut u8>::new(ptr::null_mut()); + /// Radium::fetch_or(&cell, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_or(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let cell = Cell::<*mut u8>::new(ptr::null_mut()); + /// Radium::fetch_xor(&cell, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_xor(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let cell = Cell::<*mut u8>::new(ptr::null_mut()); + /// Radium::fetch_add(&cell, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_add(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } + + /// ```compile_fail + /// # use std::{ptr, sync::atomic::*, cell::*}; + /// # use radium::*; + /// let cell = Cell::<*mut u8>::new(ptr::null_mut()); + /// Radium::fetch_sub(&cell, ptr::null_mut(), Ordering::Relaxed); + /// ``` + #[doc(hidden)] + fn fetch_sub(&self, _value: *mut T, _order: Ordering) -> *mut T { + unreachable!("This method statically cannot be called") + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::cell::Cell; + + #[test] + fn absent_traits() { + static_assertions::assert_not_impl_any!(bool: marker::NumericOps); + static_assertions::assert_not_impl_any!(*mut u8: marker::BitOps, marker::NumericOps); + } + + #[test] + fn present_traits() { + static_assertions::assert_impl_all!(bool: marker::BitOps); + static_assertions::assert_impl_all!(usize: marker::BitOps, marker::NumericOps); + } + + #[test] + fn always_cell() { + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell: Radium); + static_assertions::assert_impl_all!(Cell<*mut ()>: Radium); + } + + #[test] + fn always_alias() { + static_assertions::assert_impl_all!(types::RadiumBool: Radium); + static_assertions::assert_impl_all!(types::RadiumI8: Radium); + static_assertions::assert_impl_all!(types::RadiumU8: Radium); + static_assertions::assert_impl_all!(types::RadiumI16: Radium); + static_assertions::assert_impl_all!(types::RadiumU16: Radium); + static_assertions::assert_impl_all!(types::RadiumI32: Radium); + static_assertions::assert_impl_all!(types::RadiumU32: Radium); + static_assertions::assert_impl_all!(types::RadiumI64: Radium); + static_assertions::assert_impl_all!(types::RadiumU64: Radium); + static_assertions::assert_impl_all!(types::RadiumIsize: Radium); + static_assertions::assert_impl_all!(types::RadiumUsize: Radium); + static_assertions::assert_impl_all!(types::RadiumPtr<()>: Radium); + } + + #[test] + fn maybe_atom() { + if_atomic! { + if atomic(bool) { + use core::sync::atomic::*; + static_assertions::assert_impl_all!(AtomicBool: Radium); + } + if atomic(8) { + static_assertions::assert_impl_all!(AtomicI8: Radium); + static_assertions::assert_impl_all!(AtomicU8: Radium); + } + if atomic(16) { + static_assertions::assert_impl_all!(AtomicI16: Radium); + static_assertions::assert_impl_all!(AtomicU16: Radium); + } + if atomic(32) { + static_assertions::assert_impl_all!(AtomicI32: Radium); + static_assertions::assert_impl_all!(AtomicU32: Radium); + } + if atomic(64) { + static_assertions::assert_impl_all!(AtomicI64: Radium); + static_assertions::assert_impl_all!(AtomicU64: Radium); + } + if atomic(size) { + static_assertions::assert_impl_all!(AtomicIsize: Radium); + static_assertions::assert_impl_all!(AtomicUsize: Radium); + } + if atomic(ptr) { + static_assertions::assert_impl_all!(AtomicPtr<()>: Radium); + } + } + } +} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..9f0a623 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,178 @@ +#[doc(hidden)] +#[macro_export] +#[cfg(radium_atomic_8)] +macro_rules! __radium_if_atomic_8 { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($a)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(radium_atomic_8))] +macro_rules! __radium_if_atomic_8 { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($b)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(radium_atomic_16)] +macro_rules! __radium_if_atomic_16 { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($a)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(radium_atomic_16))] +macro_rules! __radium_if_atomic_16 { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($b)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(radium_atomic_32)] +macro_rules! __radium_if_atomic_32 { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($a)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(radium_atomic_32))] +macro_rules! __radium_if_atomic_32 { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($b)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(radium_atomic_64)] +macro_rules! __radium_if_atomic_64 { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($a)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(radium_atomic_64))] +macro_rules! __radium_if_atomic_64 { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($b)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(radium_atomic_ptr)] +macro_rules! __radium_if_atomic_ptr { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($a)* } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(radium_atomic_ptr))] +macro_rules! __radium_if_atomic_ptr { + ( [ $( $a:tt )* ] [ $( $b:tt )* ] ) => { $($b)* } +} + +/// Conditional compilation based on the presence of atomic instructions. +/// +/// This macro allows you to write `if`/`else` clauses, evaluated at +/// compile-time, that test the presence of atomic instructions and preserve or +/// destroy their guarded code accordingly. +/// +/// The `if atomic(WIDTH)` test preserves the contents of its block when the +/// target architecture has atomic instructions for the requested `WIDTH`, and +/// removes them from the syntax tree when the target does not. If an `else` +/// clause is provided, the contents of the `else` block are used as a +/// substitute when the `if` is destroyed. +/// +/// This macro can be used in any position. When it is used in item or statement +/// position, it can contain multiple `if` clauses, and each will be evaluated +/// in turn. Expression and type positions can only accept exactly one code +/// span, and so may only have exactly one `if`/`else` clause. An `else` clause +/// is required here so that the macro will always expand to something; an empty +/// expansion is a parse error. +/// +/// # Macro Syntax +/// +/// The macro contents `if atomic() {} else {}` are part of the macro +/// invocation. Only the contents of the two blocks are actual Rust code. +/// +/// The acceptable arguments to `atomic()` are: +/// +/// - `8` +/// - `16` +/// - `32` +/// - `64` +/// - `ptr` +/// - `bool`: alias for `8` +/// - `size`: alias for `ptr` +/// +/// In addition, the `atomic()` test can be inverted, as `!atomic()`, to reverse +/// the preserve/destroy behavior of the `if` and `else` blocks. +/// +/// # Examples +/// +/// This demonstrates the use of `if_atomic!` to produce multiple statements, +/// and then to produce a single type-name. +/// +/// ```rust +/// radium::if_atomic! { +/// if atomic(size) { use core::sync::atomic::AtomicUsize; } +/// if !atomic(size) { use core::cell::Cell; } +/// } +/// +/// struct RadiumRc { +/// strong: radium::if_atomic! { +/// if atomic(ptr) { AtomicUsize } +/// else { Cell } +/// }, +/// weak: radium::types::RadiumUsize, +/// data: T, +/// } +/// ``` +#[macro_export] +macro_rules! if_atomic { + ( if atomic(8) { $($a:tt)* } $( else { $($b:tt)* } )? $( if $($rest:tt)* )? ) => { + $crate::__radium_if_atomic_8! { + [ $($a)* ] [ $( $($b)* )? ] + } + $($crate::if_atomic! { if $($rest)* })? + }; + + ( if atomic(16) { $($a:tt)* } $( else { $($b:tt)* } )? $( if $($rest:tt)* )? ) => { + $crate::__radium_if_atomic_16! { + [ $($a)* ] [ $( $($b)* )? ] + } + $( $crate::if_atomic! { if $($rest)* } )? + }; + + ( if atomic(32) { $($a:tt)* } $( else { $($b:tt)* } )? $( if $($rest:tt)* )? ) => { + $crate::__radium_if_atomic_32! { + [ $($a)* ] [ $( $($b)* )? ] + } + $( $crate::if_atomic! { if $($rest)* } )? + }; + + ( if atomic(64) { $($a:tt)* } $( else { $($b:tt)* } )? $( if $($rest:tt)* )? ) => { + $crate::__radium_if_atomic_64! { + [ $($a)* ] [ $( $($b)* )? ] + } + $( $crate::if_atomic! { if $($rest)* } )? + }; + + ( if atomic(ptr) { $($a:tt)* } $( else { $($b:tt)* } )? $( if $($rest:tt)* )? ) => { + $crate::__radium_if_atomic_ptr! { + [ $($a)* ] [ $( $($b)* )? ] + } + $( $crate::if_atomic! { if $($rest)* } )? + }; + + ( if atomic(bool) $($rest:tt)* ) => { + $crate::if_atomic! { if atomic(8) $($rest)* } + }; + + ( if atomic(size) $($rest:tt)* ) => { + $crate::if_atomic! { if atomic(ptr) $($rest)* } + }; + + ( if ! atomic( $t:tt ) { $($a:tt)* } $( else { $($b:tt)* } )? $( if $($rest:tt)* )? ) => { + $crate::if_atomic! { + if atomic($t) { $( $($b)* )? } else { $($a)* } $( if $($rest)* )? + } + }; +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..c73b0db --- /dev/null +++ b/src/types.rs @@ -0,0 +1,78 @@ +//! Best-effort atomic types +//! +//! This module exports `RadiumType` aliases that map to the `AtomicType` on +//! targets that have it, or `Cell` on targets that do not. This alias can +//! be used as a consistent name for crates that need portable names for +//! non-portable types. + +/// Best-effort atomic `bool` type. +pub type RadiumBool = if_atomic! { + if atomic(bool) { core::sync::atomic::AtomicBool } + else { core::cell::Cell } +}; + +/// Best-effort atomic `i8` type. +pub type RadiumI8 = if_atomic! { + if atomic(8) { core::sync::atomic::AtomicI8 } + else { core::cell::Cell } +}; + +/// Best-effort atomic `u8` type. +pub type RadiumU8 = if_atomic! { + if atomic(8) { core::sync::atomic::AtomicU8 } + else { core::cell::Cell } +}; + +/// Best-effort atomic `i16` type. +pub type RadiumI16 = if_atomic! { + if atomic(16) { core::sync::atomic::AtomicI16 } + else { core::cell::Cell } +}; + +/// Best-effort atomic `u16` type. +pub type RadiumU16 = if_atomic! { + if atomic(16) { core::sync::atomic::AtomicU16 } + else { core::cell::Cell } +}; + +/// Best-effort atomic `i32` type. +pub type RadiumI32 = if_atomic! { + if atomic(32) { core::sync::atomic::AtomicI32 } + else { core::cell::Cell } +}; + +/// Best-effort atomic `u32` type. +pub type RadiumU32 = if_atomic! { + if atomic(32) { core::sync::atomic::AtomicU32 } + else { core::cell::Cell } +}; + +/// Best-effort atomic `i64` type. +pub type RadiumI64 = if_atomic! { + if atomic(64) { core::sync::atomic::AtomicI64 } + else { core::cell::Cell } +}; + +/// Best-effort atomic `u64` type. +pub type RadiumU64 = if_atomic! { + if atomic(64) { core::sync::atomic::AtomicU64 } + else { core::cell::Cell } +}; + +/// Best-effort atomic `isize` type. +pub type RadiumIsize = if_atomic! { + if atomic(size) { core::sync::atomic::AtomicIsize } + else { core::cell::Cell } +}; + +/// Best-effort atomic `usize` type. +pub type RadiumUsize = if_atomic! { + if atomic(size) { core::sync::atomic::AtomicUsize } + else { core::cell::Cell } +}; + +/// Best-effort atomic pointer type. +pub type RadiumPtr = if_atomic! { + if atomic(ptr) { core::sync::atomic::AtomicPtr } + else { core::cell::Cell<*mut T> } +}; -- 2.34.1