radium-1.1.0/.cargo_vcs_info.json0000644000000001440000000000100123130ustar { "git": { "sha1": "68ab1dce6ad46442fe8681df75674737120ab115" }, "path_in_vcs": "radium" }radium-1.1.0/Cargo.lock0000644000000014540000000000100102730ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "portable-atomic" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" [[package]] name = "radium" version = "1.1.0" dependencies = [ "cfg-if", "portable-atomic", "static_assertions", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" radium-1.1.0/Cargo.toml0000644000000024770000000000100103240ustar # 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 = "2021" rust-version = "1.60" name = "radium" version = "1.1.0" include = [ "Cargo.toml", "src/**/*.rs", "README.md", "doc/**/*.md", "LICENSE.txt", ] description = "Portable interfaces for maybe-atomic types" homepage = "https://ferrilab.github.io/ferrilab" documentation = "https://docs.rs/radium" readme = "README.md" keywords = [ "atomic", "cell", "sync", "generic", "trait", ] categories = [ "concurrency", "no-std", ] license = "MIT" repository = "https://github.com/ferrilab/ferrilab" [dependencies.cfg-if] version = "1" [dependencies.portable-atomic] version = "1" optional = true default-features = false [dev-dependencies.static_assertions] version = "1.1" [features] portable-atomic = ["dep:portable-atomic"] portable-atomic-fallback = [ "portable-atomic", "portable-atomic/fallback", ] radium-1.1.0/Cargo.toml.orig000064400000000000000000000014660072674642500140320ustar 00000000000000[package] name = "radium" version = "1.1.0" license = "MIT" readme = "README.md" repository = "https://github.com/ferrilab/ferrilab" homepage = "https://ferrilab.github.io/ferrilab" documentation = "https://docs.rs/radium" description = "Portable interfaces for maybe-atomic types" keywords = [ "atomic", "cell", "sync", "generic", "trait", ] categories = [ "concurrency", "no-std", ] include = [ "Cargo.toml", "src/**/*.rs", "README.md", "doc/**/*.md", "LICENSE.txt", ] edition = "2021" rust-version = "1.60" [features] portable-atomic = [ "dep:portable-atomic", ] portable-atomic-fallback = [ "portable-atomic", "portable-atomic/fallback", ] [dependencies] cfg-if = "1" [dependencies.portable-atomic] version = "1" optional = true default-features = false [dev-dependencies] static_assertions = "1.1" radium-1.1.0/LICENSE.txt000064400000000000000000000020670072674642500127640ustar 00000000000000MIT 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. radium-1.1.0/README.md000064400000000000000000000160140072674642500124150ustar 00000000000000
# Radium [![Latest Version][version_img]][crate_link] [![MSRV][msrv_img]][crate_link] [![Documentation][docs_img]][docs_link] [![Crate Downloads][downloads_img]][crate_link]
Radium provides unifying abstractions and graceful degradation for code that requires shared-mutability, but does not necessarily require hardware-level atomicity to provide it. The primary export is the [`Radium`] trait. This is implemented on all of the types in the standard library’s [`atomic`] module, as well as the [`Cell`] wrappers over `bool`, the integers, and mutable pointers. Your code can be generic over the `Radium` trait and use a stable and consistent API, and permit client code to provide atomic or non-atomic types as they are able. Additionally, Radium provides three type families with varying guarantees of atomic behavior: [`Atom`] wraps the standard library atomics, and only accepts `T` parameters where the target has an `AtomicT` type; [`Isotope`] accepts any of the types which could be atomic, and wraps atomics where they exist and silently decays to `Cell` where they do not; and [`Radon`] wraps `Cell`. All three of these types have no API except for implementing `Radium`, `Debug`, `Default`, and `From`, so your code can switch between them without needing to worry about changing usage. Lastly, Radium provides `RadiumT` type aliases matching all of the `AtomicT` type names in the standard library. Each of these aliases forwards to its atomic variant when it exists, and to `Cell` when it does not. Your code can use these names to be portable across targets with varying levels of atomic support without having to worry about the fact that `AtomicT` symbols vanish on targets that do not have the requisite atomic instructions. The Rust compiler [stabilized][0] the `cfg(target_has_atomic)` test in version 1.60. This is now the MSRV for Radium 1.0. The version-0 series will stay supported for the indeterminate future to allow for pre-1.60 projects to continue to use it. The `radium::if_atomic!` macro allows projects to simulate `#[cfg(target_has_atomic)]` in version-0, but is removed in version-1. This crate is `#![no_std]`-compatible, as it relies solely on the `core::sync::atomic` and `core::cell` modules. ## Versioning 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. As of Rust 1.60, support for 128-bit atomics is still unstable. Since Radium commits to being usable on the stable release series, it does not support 128-bit atomics. As a compromise, `Cell<{i,u}128>` *is* integrated with Radium to prepare for stabilizaation in the future. If 128-bit atomics are removed from the standard library without stabilization, Radium will remove support for `Cell<{i,u}128>` in a major-version increase. ## Non-Standard Implementors In addition to the Rust standard library `Cell` and `Atomic` types, we also provide an implementation for the [`portable-atomic`] crate. However, the `portable-atomic` implementation cannot compile on a select few targets. As of 1.60, they are: - `thumbv6m-none-eabi` - `riscv32i-unknown-none-elf` - `riscv32imc-unknown-none-elf` These targets have 32-bit atomic load and store instructions, but do not have read/modify/write instructions. Since `Radium` demands RMU behavior, and `portable-atomic` does not provide it even in software (the `.fetch_action` methods are all missing), we do not attempt to handle these targets gracefully and simply allow the compile to fail. Do not use the `portable-atomic` feature when compiling for these targets. We disable all `portable-atomic` features, including the default-on `fallback` feature. This causes `portable-atomic` to only generate symbols that match what the standard library provides on that target. If you enable `portable-atomic/fallback` in your own crate, then these symbols will exist, but `radium` will not be able to see them because `#[cfg(feature = "...")]` cannot query *other* crates’ enabled feature set. You will need to set radium’s `portable-atomic-fallback` feature to get `Radium` implementations for atomic operations wider than what the target instruction set supports. ## Pre-1.60 Target Discovery Because the compiler did not make atomic support on targets accessible to libraries, Radium used a build script to detect the target architecture and emit its own directives that marked the presence or absence of an atomic integer. We accomplished this by reading the compiler’s target information records and copying the information directly into our build script. If Radium v0 does not work for your architecture, please update the build script to handle your target string and submit a pull request against the v0 branch. 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 environment 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 attempted parse. ---- ## Project Origins **@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/radium "Crates.io package" [docs_img]: https://img.shields.io/docsrs/radium/latest.svg?style=for-the-badge "Radium documentation badge" [docs_link]: https://docs.rs/radium "Radium documentation" [downloads_img]: https://img.shields.io/crates/dv/radium.svg?style=for-the-badge "Crate downloads" [msrv_img]: https://img.shields.io/badge/MSRV-1.60-f46623?style=for-the-badge&logo=rust "Minimum Supported Rust Version: 1.60" [version_img]: https://img.shields.io/crates/v/radium?color=f46623&style=for-the-badge "Radium version badge" [`Atom`]: https://docs.rs/radium/latest/radium/types/struct.Atom.html [`Cell`]: https://doc.rust-lang.org/core/cell/struct.Cell.html [`Isotope`]: https://docs.rs/radium/latest/radium/types/struct.Isotope.html [`Radium`]: https://docs.rs/radium/latest/radium/trait.Radium.html [`Radon`]: https://docs.rs/radium/latest/radium/types/struct.Radon.html [`atomic`]: https://doc.rust-lang.org/core/sync/atomic [`portable-atomic`]: https://docs.rs/portable-atomic/1 [0]: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1600-2022-04-07 radium-1.1.0/doc/atom.md000064400000000000000000000012250072674642500131630ustar 00000000000000# Atomic Primitives This family takes a Rust primitive (`bool`, integer, or pointer) as a type parameter and acts as the corresponding `AtomicT` type. Attempting to construct an `Atom` on a target that has no `AtomicT` will cause a compilation failure stating that `T` does not implement the `Atomic` trait. The [`Isotope`] sibling type will never fail to compile, but in exchange does not guarantee atomic behavior. This type implements the [`Radium`] API, as well as the `Debug`, `Default`, and `From` traits that the standard library atomics implement. It has no other API surface. [`Isotope`]: crate::types::Isotope [`Radium`]: crate::Radium radium-1.1.0/doc/isotope.md000064400000000000000000000014310072674642500137040ustar 00000000000000# Best-Effort Atomic Primitives This family takes a Rust primitive (`bool`, integer, or pointer) as a type parameter and acts as the corresponding `RadiumT` type. Unlike the [`Atom`] sibling type, this type will never produce a compiler error when instantiated (with a valid primitive). When the requisite atomic support is missing on the target architecture, it falls back to acting as a `Cell`. This type implements the [`Radium`] API, as well as the `Debug`, `Default`, and `From` traits that the standard library atomics implement. It has no other API surface, and in particular *does not* attempt to follow the `Cell` API. See also [`Radon`], which is always strictly non-atomic. [`Atom`]: crate::types::Atom [`Radium`]: crate::Radium [`Radon`]: crate::types::Radon radium-1.1.0/doc/radium.md000064400000000000000000000050040072674642500135030ustar 00000000000000# Unified Shared-Mutable API The `Radium` trait is a common interface for shared-mutable types. We provide implementations for the `AtomicT` and `Cell` types in the standard library, as well as our [alternative types][types]. It mirrors the API of the Rust [atomic] types. You should consult the Rust standard library documentation for the correct usage of all methods. While we refer to, and draw on, the Rust project’s work, we do not guarantee keeping our language up to date with it. Some of the `Radium` methods are gated on marker types in order to prevent their use when the underlying primitive does not support them. For instance, pointers do not (at time of writing) support atomic bit-wise or numeric operations, and so cannot be used with any of the `.fetch_modify()` methods. Attempting to call these methods will cause a compiler error when the underlying primitive type is unsuitable. ## Usage You should use this trait as a type parameter in your API when you want to accept *something* that supports shared-mutability, but you don’t need to care about what it is. You will likely want to specify the `Item` to be a known type, by writing this bound: `>` where `T` is another generic parameter or a named primitive. Radium does *not* provide any unified trait system for the Rust primitives! If you want to accept `Radium::Item` as a generic parameter, you will need to use another crate (for instance, [`funty`]) to describe behavior over generic primitives. ## Non-Usage If you do not wish to expose caller-specified shared-mutability in your API, you should instead use the [`radium::types`][types] module. The types in that module all implement `Radium` as their only behavior, but may be easier to use when you are describing particular data types. ## Safety This trait is marked as `unsafe` to implement, because it abstracts over types which can be mutated through a shared reference. The implementor is required to correctly synchronize writes that occur through `Radium` methods, and in particular, to never violate the Rust language’s rules about the invalid production of unique references. ## Implementation While we do not enforce any restrictions on `Radium` implementors, only types which are transparent wrappers over a Rust primitive should implement this. Out-of-line guards such as `Mutex` can technically satisfy its API requirements, but are not likely to be useful candidate types for these uses. [atomic]: core::sync::atomic [types]: crate::types [`funty`]: //crates.io/crates/funty radium-1.1.0/doc/radon.md000064400000000000000000000017320072674642500133310ustar 00000000000000# Non-Atomic Primitives This family takes a Rust primitive (`bool`, integer, or pointer) as a type parameter and wraps it in a `Cell`. Like `Atom` and `Isotope`, it implements *only* the [`Radium`] API (and `Debug`, `Default`, and `From`), and as such is suitable for cases where a crate wants to turn off atomic usage entirely, while guaranteeing that swapping out types will not cause a compilation failure. ## Examples Consider a crate with an `"atomic"` feature. It might decide to *attempt* atomic behavior when this flag is on, and unconditionally deny it when the flag is off: ```rust #[cfg(feature = "atomic")] pub type MyAtom = radium::types::Isotope; #[cfg(not(feature = "atomic"))] pub type MyAtom = radium::types::Radon; ``` ## Behind the Name Radium decays into radon, and the `Radon` type is a “decayed” `Radium` implementor. Radon gas is also poisonous, and `Radon` poisons your codebase against multithreading. [`Radium`]: crate::Radium radium-1.1.0/doc/types.md000064400000000000000000000024400072674642500133670ustar 00000000000000# Aliases and Type Families This module provides `RadiumT` aliases that correspond to `AtomicT` from the standard library, and resolve to `AtomicT` when it exists and `Cell` when it does not. In addition, newtype structs `Atom` and `Isotope` correspond to (and contain) `AtomicT` and `RadiumT`, respectively. These newtypes are designed to be used in cases where you are generic over a primitive and want to plug it into a shared-mutable wrapper type without having to specifically name one of the `AtomicT` or `RadiumT` individual names. Lastly, the `Radon` newtype struct wraps `Cell` and only implements the `Radium` API, mirroring `Atom` and `Isotope`. This type exists so that client crates can switch out types based on a crate feature to disable atomics, and guarantee that their API will continue to function in every regard (except for losing `Sync` impls). ## Examples ```rust use radium::{Radium, types::*}; #[cfg(target_has_atomic = "ptr")] let a = Atom::new(0usize); let b = Isotope::new(1usize); let c = Radon::new(2usize); // when atomics are not disabled, use best-effort #[cfg(feature = "atomics")] pub type MyIsotope = Isotope; // when atomics are fully disabled, enforce use of `Cell` #[cfg(not(feature = "atomics"))] pub type MyIsotope = Radon; ``` radium-1.1.0/src/lib.rs000064400000000000000000000576740072674642500130620ustar 00000000000000#![doc = include_str!("../README.md")] #![no_std] #![deny(unconditional_recursion)] pub mod marker; pub mod portable; pub mod types; use core::{ cell::Cell, sync::atomic::*, }; use crate::marker::*; pub use crate::types::{ Atom, Isotope, Radon, }; #[doc = include_str!("../doc/radium.md")] pub unsafe trait Radium { /// The primitive type that this implementor makes shared-mutable. type Item: Copy + PartialEq; /// Creates a new value of this type. fn new(value: Self::Item) -> Self; /// If the implementor is atomic, this calls [`atomic::fence`] with the /// given `Ordering`; otherwise, it does nothing. /// /// [`atomic::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 because consuming by value ensures that no other references /// exist. fn into_inner(self) -> Self::Item; /// Loads a value from this object. /// /// Non-atomic implementors ignore the ordering value. /// /// See also: [`AtomicUsize::load`]. /// /// [`AtomicUsize::load`]: core::sync::atomic::AtomicUsize::load fn load(&self, order: Ordering) -> Self::Item; /// Stores a value into this object. /// /// Non-atomic implementors ignore the ordering value. /// /// See also: [`AtomicUsize::store`]. /// /// [`AtomicUsize::store`]: core::sync::atomic::AtomicUsize::store fn store(&self, value: Self::Item, order: Ordering); /// Swaps a new value with the value stored in this object. /// /// Non-atomic implementors ignore the ordering value. /// /// See also: [`AtomicUsize::swap`]. /// /// [`AtomicUsize::swap`]: core::sync::atomic::AtomicUsize::swap fn swap(&self, value: Self::Item, order: Ordering) -> Self::Item; /// Stores a new value into this object if (and only if) the value currently /// stored in it is the same as the `current` argument. /// /// The return value is always what the object contained before the call /// entered. If it is equal to the `current` argument, then the object has /// been updated to contain `new`. /// /// Non-atomic implementors ignore the ordering value. /// /// 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 new value into this object if (and only if) the value currently /// stored in it is the same as the `current` argument. /// /// The return value is a `Result` indicating whether the new value was /// written into this object, and containing the value this object contained /// when the call entered. On success, this value is guaranteed to be equal /// to `current`. /// /// Non-atomic implementors ignore the ordering value. /// /// 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 new value into this object if (and only if) the value currently /// stored in it is the same as the `current` argument. /// /// 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 comparison succeeded and the new value was written, and /// containing the value this object contained when the call entered. /// /// Non-atomic implementors ignore the ordering value. /// /// 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 bit-wise AND on the currently-stored value and the argument. /// The result is stored into this object, and the previous value is /// returned. /// /// Non-atomic implementors ignore the ordering value. /// /// 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: BitOps; /// Performs a bit-wise NAND on the currently-stored value and the argument. /// The result is stored into this object, and the previous value is /// returned. /// /// Non-atomic implementors ignore the ordering value. /// /// 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: BitOps; /// Performs a bit-wise OR on the currently-stored value and the argument. /// The result is stored into this object, and the previous value is /// returned. /// /// Non-atomic implementors ignore the ordering value. /// /// 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: BitOps; /// Performs a bit-wise XOR on the currently-stored value and the argument. /// The result is stored into this object, and the previous value is /// returned. /// /// Non-atomic implementors ignore the ordering value. /// /// 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: BitOps; /// Adds the argument into the currently-stored value, wrapping on overflow. /// The result is stored into this object, and the previous value is /// returned. /// /// Non-atomic implementors ignore the ordering value. /// /// 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: NumericOps; /// Subtracts the argument from the currently-stored value, wrapping on /// overflow. The result is stored into this object, and the previous value /// is returned. /// /// Non-atomic implementors ignore the ordering value. /// /// 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: NumericOps; /// Finds the maximum of the currently-stored value and the argument. The /// result is stored into this object, and the previous value is returned. /// /// Non-atomic implementors ignore the ordering value. /// /// See also: [`AtomicUsize::fetch_max`]. /// /// [`AtomicUsize::fetch_max`]: core::sync::atomic::AtomicUsize::fetch_max fn fetch_max(&self, value: Self::Item, order: Ordering) -> Self::Item where Self::Item: NumericOps; /// Finds the minimum of the currently-stored value and the argument. The /// result is stored into this object, and the previous value is returned. /// /// Non-atomic implementors ignore the ordering value. /// /// See also: [`AtomicUsize::fetch_min`]. /// /// [`AtomicUsize::fetch_min`]: core::sync::atomic::AtomicUsize::fetch_min fn fetch_min(&self, value: Self::Item, order: Ordering) -> Self::Item where Self::Item: NumericOps; /// Fetches the value, and applies a function to it that may produce a new /// value. /// /// Note: this may call the generator function multiple times if the stored /// value is updated in between the fetch and store. However, when a store /// occurs successfully, the generator will have been applied only once to /// the fetched value. That is, this function will never store /// `f(f(self.load()))`. /// /// Returns `Ok(fetched)` if the generator produces `Some` new value which /// is successfully stored, or `Err(fetched)` if it produces `None`. /// /// Non-atomic implementors ignore the ordering value. /// /// 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; } /// Generates `Radium` implementation bodies. macro_rules! radium { ($($width:literal : $bit:ident $num:ident => { $($(@<$t:ident>)? $base:ty $(=> $atom:ident)?;)+ } )+) => { $( $( radium!(atom $width $bit $num $(@<$t>)? $base $(=> $atom)?); radium!(cell $width $bit $num $(@<$t>)? $base); )+ )+ }; // Trap the branch that has no named atom. (atom $width:literal $bit:ident $num:ident $(@<$t:ident>)? $base:ty) => {}; // Generate an implementation for the named atom. ( atom $width:literal $bit:ident $num:ident $(@<$t:ident>)? $base:ty => $atom:ident ) => { #[cfg(target_has_atomic = $width)] unsafe impl$(<$t>)? Radium for $atom$(<$t>)? { type Item = $base; #[inline] fn new(value: $base) -> Self { <$atom$(<$t>)?>::new(value) } #[inline] fn fence(order: Ordering) { core::sync::atomic::fence(order); } #[inline] fn get_mut(&mut self) -> &mut $base { <$atom$(<$t>)?>::get_mut(self) } #[inline] fn into_inner(self) -> $base { <$atom$(<$t>)?>::into_inner(self) } #[inline] fn load(&self, order: Ordering) -> $base { <$atom$(<$t>)?>::load(self, order) } #[inline] fn store(&self, value: $base, order: Ordering) { <$atom$(<$t>)?>::store(self, value, order); } #[inline] fn swap(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::swap(self, value, order) } #[inline] #[allow(deprecated)] fn compare_and_swap( &self, current: $base, new: $base, order: Ordering, ) -> $base { <$atom$(<$t>)?>::compare_and_swap(self, current, new, order) } #[inline] fn compare_exchange( &self, current: $base, new: $base, success: Ordering, failure: Ordering ) -> Result<$base, $base> { <$atom$(<$t>)?>::compare_exchange(self, current, new, success, failure) } #[inline] fn compare_exchange_weak( &self, current: $base, new: $base, success: Ordering, failure: Ordering, ) -> Result<$base, $base> { <$atom$(<$t>)?>::compare_exchange_weak( self, current, new, success, failure, ) } radium!(atom $bit $(@<$t>)? $atom => $base); radium!(atom $num $(@<$t>)? $atom => $base); #[inline] fn fetch_update( &self, set_order: Ordering, fetch_order: Ordering, func: F, ) -> Result<$base, $base> where F: FnMut($base) -> Option<$base>, { <$atom$(<$t>)?>::fetch_update(self, set_order, fetch_order, func) } } }; // Generate an implementation for the Cell. (cell $width:literal $bit:ident $num:ident $(@<$t:ident>)? $base:ty) => { unsafe impl$(<$t>)? Radium for Cell<$base> { type Item = $base; #[inline] fn new(value: $base) -> Self { Cell::new(value) } #[inline] fn fence(_: Ordering) {} #[inline] fn get_mut(&mut self) -> &mut $base { Cell::get_mut(self) } #[inline] fn into_inner(self) -> $base { Cell::into_inner(self) } #[inline] fn load(&self, _: Ordering) -> $base { Cell::get(self) } #[inline] fn store(&self, value: $base, _: Ordering) { Cell::set(self, value); } #[inline] fn swap(&self, value: $base, _: Ordering) -> $base { Cell::replace(self, value) } #[inline] #[allow(deprecated)] fn compare_and_swap( &self, current: $base, new: $base, _: Ordering, ) -> $base { let old = Cell::get(self); if old == current { Cell::set(self, new); } old } #[inline] fn compare_exchange( &self, current: $base, new: $base, _: Ordering, _: Ordering ) -> Result<$base, $base> { let old = Cell::get(self); if old == current { Cell::set(self, new); Ok(old) } else { Err(old) } } #[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) } radium!(cell $bit $(@<$t>)? $base); radium!(cell $num $(@<$t>)? $base); #[inline] fn fetch_update(&self, _: Ordering, _: Ordering, mut func: F) -> Result<$base, $base> where F: FnMut($base) -> Option<$base>, { let old = Cell::get(self); match func(old) { Some(new) => { Cell::set(self, new); Ok(old) }, None => Err(old), } } } }; // Forward to the atomic RMU functions. (atom bit $(@<$t:ident>)? $atom:ident => $base:ty) => { #[inline] fn fetch_and(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_and(self, value, order) } #[inline] fn fetch_nand(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_nand(self, value, order) } #[inline] fn fetch_or(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_or(self, value, order) } #[inline] fn fetch_xor(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_xor(self, value, order) } }; (atom num $(@<$t:ident>)? $atom:ident => $base:ty) => { #[inline] fn fetch_add(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_add(self, value, order) } #[inline] fn fetch_sub(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_sub(self, value, order) } #[inline] fn fetch_max(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_max(self, value, order) } #[inline] fn fetch_min(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_min(self, value, order) } }; // Implement non-atomic RMU functions. (cell bit $(@<$t:ident>)? $base:ty) => { #[inline] fn fetch_and(&self, value: $base, _: Ordering) -> $base { let old = Cell::get(self); Cell::set(self, old & value); old } #[inline] fn fetch_nand(&self, value: $base, _: Ordering) -> $base { let old = Cell::get(self); Cell::set(self, !(old & value)); old } #[inline] fn fetch_or(&self, value: $base, _: Ordering) -> $base { let old = Cell::get(self); Cell::set(self, old | value); old } #[inline] fn fetch_xor(&self, value: $base, _: Ordering) -> $base { let old = Cell::get(self); Cell::set(self, old ^ value); old } }; (cell num $(@<$t:ident>)? $base:ty) => { #[inline] fn fetch_add(&self, value: $base, _: Ordering) -> $base { let old = Cell::get(self); Cell::set(self, old.wrapping_add(value)); old } #[inline] fn fetch_sub(&self, value: $base, _: Ordering) -> $base { let old = Cell::get(self); Cell::set(self, old.wrapping_sub(value)); old } #[inline] fn fetch_max(&self, value: $base, _: Ordering) -> $base { let old = Cell::get(self); Cell::set(self, core::cmp::max(old, value)); old } #[inline] fn fetch_min(&self, value: $base, _: Ordering) -> $base { let old = Cell::get(self); Cell::set(self, core::cmp::min(old, value)); old } }; // Handle stubbed-out RMU functions. (atom no_bit $(@<$t:ident>)? $atom:ident => $base:ty) => { radium!(unreachable fetch_and, fetch_nand, fetch_or, fetch_xor); }; (atom no_num $(@<$t:ident>)? $atom:ident => $base:ty) => { radium!(unreachable fetch_add, fetch_sub, fetch_max, fetch_min); }; (cell no_bit $(@<$t:ident>)? $base:ty) => { radium!(unreachable fetch_and, fetch_nand, fetch_or, fetch_xor); }; (cell no_num $(@<$t:ident>)? $base:ty) => { radium!(unreachable fetch_add, fetch_sub, fetch_max, fetch_min); }; // Generate forwarding functions for `Atom` and `Isotope`. (wrappers) => { #[inline] fn new(value: T) -> Self { Self { inner: Radium::new(value), } } #[inline] fn get_mut(&mut self) -> &mut T { Radium::get_mut(&mut self.inner) } #[inline] fn into_inner(self) -> T { Radium::into_inner(self.inner) } #[inline] fn load(&self, order: Ordering) -> T { Radium::load(&self.inner, order) } #[inline] fn store(&self, value: T, order: Ordering) { Radium::store(&self.inner, value, order); } #[inline] fn swap(&self, value: T, order: Ordering) -> T { Radium::swap(&self.inner, value, order) } #[inline] #[allow(deprecated)] fn compare_and_swap( &self, current: T, new: T, order: Ordering, ) -> T { Radium::compare_and_swap(&self.inner, current, new, order) } #[inline] fn compare_exchange( &self, current: T, new: T, success: Ordering, failure: Ordering, ) -> Result { Radium::compare_exchange( &self.inner, current, new, success, failure, ) } #[inline] fn compare_exchange_weak( &self, current: T, new: T, success: Ordering, failure: Ordering, ) -> Result { Radium::compare_exchange_weak( &self.inner, current, new, success, failure, ) } #[inline] fn fetch_and(&self, value: T, order: Ordering) -> T where T: BitOps, { Radium::fetch_and(&self.inner, value, order) } #[inline] fn fetch_nand(&self, value: T, order: Ordering) -> T where T: BitOps, { Radium::fetch_nand(&self.inner, value, order) } #[inline] fn fetch_or(&self, value: T, order: Ordering) -> T where T: BitOps, { Radium::fetch_or(&self.inner, value, order) } #[inline] fn fetch_xor(&self, value: T, order: Ordering) -> T where T: BitOps, { Radium::fetch_xor(&self.inner, value, order) } #[inline] fn fetch_add(&self, value: T, order: Ordering) -> T where T: NumericOps, { Radium::fetch_add(&self.inner, value, order) } #[inline] fn fetch_sub(&self, value: T, order: Ordering) -> T where T: NumericOps, { Radium::fetch_sub(&self.inner, value, order) } #[inline] fn fetch_max(&self, value: T, order: Ordering) -> T where T: NumericOps, { Radium::fetch_max(&self.inner, value, order) } #[inline] fn fetch_min(&self, value: T, order: Ordering) -> T where T: NumericOps, { Radium::fetch_min(&self.inner, value, order) } #[inline] fn fetch_update( &self, set_order: Ordering, fetch_order: Ordering, func: F, ) -> Result where F: FnMut(T) -> Option, { Radium::fetch_update(&self.inner, set_order, fetch_order, func) } }; // Generate stubs for the conditionally-unreachable methods. (unreachable $($n:ident),+ $(,)?) => { $( fn $n(&self, _: Self::Item, _: Ordering) -> Self::Item { unreachable!( "This function is statically guaranteed to never be callable", ); } )+ }; } radium! { "8": bit no_num => { bool => AtomicBool; } "8": bit num => { i8 => AtomicI8; u8 => AtomicU8; } "16": bit num => { i16 => AtomicI16; u16 => AtomicU16; } "32": bit num => { i32 => AtomicI32; u32 => AtomicU32; } "64": bit num => { i64 => AtomicI64; u64 => AtomicU64; } "128": bit num => { i128; // => AtomicI128; // when this stabilizes u128; // => AtomicU128; // when this stabilizes } "ptr": bit num => { isize => AtomicIsize; usize => AtomicUsize; } "ptr": no_bit no_num => { @ *mut T => AtomicPtr; } } unsafe impl Radium for Atom where T: Atomic + PartialEq, T::Atom: Radium, { type Item = T; radium!(wrappers); fn fence(order: Ordering) { core::sync::atomic::fence(order); } } unsafe impl Radium for Isotope where T: Nuclear + PartialEq, T::Nucleus: Radium, { type Item = T; radium!(wrappers); fn fence(order: Ordering) { ::fence(order); } } unsafe impl Radium for Radon where T: Nuclear + PartialEq, Cell: Radium, { type Item = T; radium!(wrappers); fn fence(_: Ordering) { } } #[cfg(test)] mod tests { #[allow(unused_imports)] use core::sync::atomic::*; use static_assertions::*; use super::*; use crate::types::*; #[test] fn absent_traits() { assert_not_impl_any!(bool: NumericOps); assert_not_impl_any!(*mut u8: BitOps, NumericOps); } #[test] fn present_traits() { assert_impl_all!(bool: BitOps); assert_impl_all!(usize: BitOps, NumericOps); } #[test] fn always_cell() { assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell: Radium); assert_impl_all!(Cell<*mut ()>: Radium); } #[test] fn always_alias() { assert_impl_all!(RadiumBool: Radium); assert_impl_all!(RadiumI8: Radium); assert_impl_all!(RadiumU8: Radium); assert_impl_all!(RadiumI16: Radium); assert_impl_all!(RadiumU16: Radium); assert_impl_all!(RadiumI32: Radium); assert_impl_all!(RadiumU32: Radium); assert_impl_all!(RadiumI64: Radium); assert_impl_all!(RadiumU64: Radium); assert_impl_all!(RadiumIsize: Radium); assert_impl_all!(RadiumUsize: Radium); assert_impl_all!(RadiumPtr<()>: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope: Radium); assert_impl_all!(Isotope<*mut ()>: Radium); } #[test] fn maybe_atom() { #[cfg(target_has_atomic = "8")] { assert_impl_all!(AtomicBool: Radium); assert_impl_all!(AtomicI8: Radium); assert_impl_all!(AtomicU8: Radium); assert_impl_all!(Atom: Radium); assert_impl_all!(Atom: Radium); assert_impl_all!(Atom: Radium); } #[cfg(target_has_atomic = "16")] { assert_impl_all!(AtomicI16: Radium); assert_impl_all!(AtomicU16: Radium); assert_impl_all!(Atom: Radium); assert_impl_all!(Atom: Radium); } #[cfg(target_has_atomic = "32")] { assert_impl_all!(AtomicI32: Radium); assert_impl_all!(AtomicU32: Radium); assert_impl_all!(Atom: Radium); assert_impl_all!(Atom: Radium); } #[cfg(target_has_atomic = "64")] { assert_impl_all!(AtomicI64: Radium); assert_impl_all!(AtomicU64: Radium); assert_impl_all!(Atom: Radium); assert_impl_all!(Atom: Radium); } #[cfg(target_has_atomic = "ptr")] { assert_impl_all!(AtomicIsize: Radium); assert_impl_all!(AtomicUsize: Radium); assert_impl_all!(AtomicPtr<()>: Radium); assert_impl_all!(Atom: Radium); assert_impl_all!(Atom: Radium); assert_impl_all!(Atom<*mut ()>: Radium); } } } radium-1.1.0/src/marker.rs000064400000000000000000000032460072674642500135570ustar 00000000000000//! Marker traits used to gate `Radium` methods and newtype parameters. use core::ops::{ Add, BitAnd, BitOr, BitXor, Not, Sub, }; use crate::Radium; /// Indicates that the type supports bit-wise operations. pub trait BitOps: Sized + BitAnd + BitOr + BitXor + Not { } /// Indicates that the type supports integer operations. pub trait NumericOps: BitOps + Add + Sub + PartialEq + Ord { } macro_rules! mark { ($($t:ty => $($u:ty),+ $(,)?);+ $(;)?) => { $( $( impl $t for $u {} )+ )+ }; } mark! { BitOps => bool, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize; NumericOps => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize; } /// Relates a primitive type to its corresponding atomic type. /// /// This is only implemented when the corresponding atomic type exists. pub trait Atomic: Copy { /// The `AtomicT` type corresponding to `Self`. type Atom: Radium + Send + Sync; } /// Relates a primitive type to its corresponding best-effort atomic type. /// /// This is always implemented; however, because `Nucleus` uses `RadiumT` rather /// than `AtomicT`, the destination type might wind up being `Cell`. /// /// ## Behind the Name /// /// Atoms and (eukaryotic) cells both have a nucleus. Technically each /// *implementor* of this trait is the nucleus, and the destination of the /// associated type is the nuclear thing that possesses a nucleus. Sorry this /// codebase isn’t a perfect reflection of biology and physics. pub trait Nuclear: Copy { /// The `RadiumT` type corresponding to `Self`. type Nucleus: Radium; } radium-1.1.0/src/portable.rs000064400000000000000000000111750072674642500141060ustar 00000000000000//! Implements `Radium` on the `portable-atomic` types. #![cfg(feature = "portable-atomic")] pub use portable_atomic::*; macro_rules! portable { ($($width:literal : $bit:ident $num:ident => { $($(@<$t:ident>)? $base:ty => $atom:ident;)+ })+) => { $( $( #[cfg(any( target_has_atomic = $width, feature = "portable-atomic-fallback", ))] unsafe impl$(<$t>)? crate::Radium for $atom$(<$t>)? { type Item = $base; #[inline] fn new(value: $base) -> Self { <$atom$(<$t>)?>::new(value) } #[inline] fn fence(order: Ordering) { portable_atomic::fence(order); } #[inline] fn get_mut(&mut self) -> &mut $base { <$atom$(<$t>)?>::get_mut(self) } #[inline] fn into_inner(self) -> $base { <$atom$(<$t>)?>::into_inner(self) } #[inline] fn load(&self, order: Ordering) -> $base { <$atom$(<$t>)?>::load(self, order) } #[inline] fn store(&self, value: $base, order: Ordering) { <$atom$(<$t>)?>::store(self, value, order); } #[inline] fn swap(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::swap(self, value, order) } #[inline] fn compare_and_swap( &self, current: $base, new: $base, order: Ordering, ) -> $base { match <$atom$(<$t>)?>::compare_exchange_weak( self, current, new, order, order, ) { Ok(val) => val, Err(val) => val, } } #[inline] fn compare_exchange( &self, current: $base, new: $base, success: Ordering, failure: Ordering, ) -> Result<$base, $base> { <$atom$(<$t>)?>::compare_exchange( self, current, new, success, failure, ) } #[inline] fn compare_exchange_weak( &self, current: $base, new: $base, success: Ordering, failure: Ordering, ) -> Result<$base, $base> { <$atom$(<$t>)?>::compare_exchange_weak( self, current, new, success, failure, ) } portable!($bit $(@<$t>)? $atom => $base); portable!($num $(@<$t>)? $atom => $base); #[inline] fn fetch_update( &self, set_order: Ordering, fetch_order: Ordering, mut func: F, ) -> Result<$base, $base> where F: FnMut($base) -> Option<$base>, { loop { let old = <$atom$(<$t>)?>::load(self, fetch_order); let new = func(old).ok_or(old)?; match <$atom$(<$t>)?>::compare_exchange_weak( self, old, new, set_order, fetch_order, ) { Ok(val) => return Ok(val), Err(_) => continue, } } } } )+ )+ }; (bit $(@<$t:ident>)? $atom:ident => $base:ty) => { #[inline] fn fetch_and(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_and(self, value, order) } #[inline] fn fetch_nand(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_nand(self, value, order) } #[inline] fn fetch_or(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_or(self, value, order) } #[inline] fn fetch_xor(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_xor(self, value, order) } }; (num $(@<$t:ident>)? $atom:ident => $base:ty) => { #[inline] fn fetch_add(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_add(self, value, order) } #[inline] fn fetch_sub(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_sub(self, value, order) } #[inline] fn fetch_max(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_max(self, value, order) } #[inline] fn fetch_min(&self, value: $base, order: Ordering) -> $base { <$atom$(<$t>)?>::fetch_min(self, value, order) } }; (no_bit $(@<$t:ident>)? $atom:ident => $base:ty) => { portable!(unreachable fetch_and, fetch_nand, fetch_or, fetch_xor); }; (no_num $(@<$t:ident>)? $atom:ident => $base:ty) => { portable!(unreachable fetch_add, fetch_sub, fetch_max, fetch_min); }; (unreachable $($n:ident),+ $(,)?) => { $( fn $n(&self, _: Self::Item, _: Ordering) -> Self::Item { unreachable!( "This function is statically guaranteed to never be callable", ); } )+ }; } portable! { "8": bit no_num => { bool => AtomicBool; } "8": bit num => { i8 => AtomicI8; u8 => AtomicU8; } "16": bit num => { i16 => AtomicI16; u16 => AtomicU16; } "32": bit num => { i32 => AtomicI32; u32 => AtomicU32; } "64": bit num => { i64 => AtomicI64; u64 => AtomicU64; } "128": bit num => { i128 => AtomicI128; u128 => AtomicU128; } "ptr": bit num => { isize => AtomicIsize; usize => AtomicUsize; } "ptr": no_bit no_num => { @ *mut T => AtomicPtr; } } radium-1.1.0/src/types.rs000064400000000000000000000243160072674642500134430ustar 00000000000000#![doc = include_str!("../doc/types.md")] #[allow(unused_imports)] use core::{ cell::Cell, fmt::{ self, Debug, Formatter, }, sync::atomic::Ordering, }; use crate::{ marker::{ Atomic, Nuclear, }, Radium, }; #[repr(transparent)] #[doc = include_str!("../doc/atom.md")] pub struct Atom where T: Atomic, T::Atom: Radium, { pub(crate) inner: T::Atom, } impl Debug for Atom where T: Atomic + Debug, T::Atom: Radium + Debug, { #[inline] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Debug::fmt(&self.inner, fmt) } } impl Default for Atom where T: Atomic + Default, T::Atom: Radium + Default, { #[inline] fn default() -> Self { Self { inner: Default::default(), } } } impl From for Atom where T: Atomic, T::Atom: Radium + From, { #[inline] fn from(val: T) -> Self { Self { inner: From::from(val), } } } #[repr(transparent)] #[doc = include_str!("../doc/isotope.md")] pub struct Isotope where T: Nuclear, T::Nucleus: Radium, { pub(crate) inner: T::Nucleus, } impl Debug for Isotope where T: Nuclear + Debug, T::Nucleus: Radium + Debug, { #[inline] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Debug::fmt(&self.inner, fmt) } } impl Default for Isotope where T: Nuclear + Default, T::Nucleus: Radium + Default, { #[inline] fn default() -> Self { Self { inner: Default::default(), } } } impl From for Isotope where T: Nuclear, T::Nucleus: Radium + From, { #[inline] fn from(val: T) -> Self { Self { inner: From::from(val), } } } #[repr(transparent)] #[doc = include_str!("../doc/radon.md")] pub struct Radon where T: Nuclear, Cell: Radium, { pub(crate) inner: Cell, } impl Debug for Radon where T: Nuclear + Debug, Cell: Radium + Debug, { #[inline] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Debug::fmt(&self.inner, fmt) } } impl Default for Radon where T: Nuclear + Default, Cell: Radium + Default, { #[inline] fn default() -> Self { Self { inner: Default::default(), } } } impl From for Radon where T: Nuclear, Cell: Radium + From, { #[inline] fn from(val: T) -> Self { Self { inner: From::from(val), } } } /// Creates type aliases that resolve to either `AtomicT` or `Cell` depending /// on availability. macro_rules! alias { ($($width:literal => { $( $(@<$t:ident>)? $base:ty => $radium:ident $(=> $atom:ident)? );+ $(;)? })+) => { $( $( alias!(atom $width $(@<$t>)? $base => $radium $(=> $atom)?); alias!(cell $width $(@<$t>)? $base => $radium $(=> $atom)?); )+ )+ }; (atom $width:literal $(@<$t:ident>)? $base:ty => $radium:ident) => {}; (atom $width:literal $(@<$t:ident>)? $base:ty => $radium:ident => $atom:ident) => { #[doc = concat!("Best-effort atomicity for `", stringify!($base), "`.")] /// /// This target has the required atomic support. #[cfg(target_has_atomic = $width)] pub type $radium$(<$t>)? = core::sync::atomic::$atom$(<$t>)?; // If the atomic variant exists, create `Atom`. #[cfg(target_has_atomic = $width)] impl$(<$t>)? Atomic for $base { type Atom = core::sync::atomic::$atom$(<$t>)?; } }; // When an atom is provided, be conditional on target atomics. (cell $width:literal $(@<$t:ident>)? $base:ty => $radium:ident => $atom:ident) => { #[doc = concat!("Best-effort atomicity for `", stringify!($base), "`.")] /// /// This target does not have the required atomic support, and is /// falling back to `Cell`. #[cfg(not(target_has_atomic = $width))] pub type $radium$(<$t>)? = Cell<$base>; // Create `Isotope` with the generated alias. impl$(<$t>)? Nuclear for $base { type Nucleus = $radium$(<$t>)?; } }; // When an atom is not provided, unconditionally create the alias. (cell $width:literal $(@<$t:ident>)? $base:ty => $radium:ident) => { #[doc = concat!("Best-effort atomicity for `", stringify!($base), "`.")] /// /// The required atomic support is not stabilized in `core`, so this is /// unconditionally a `Cell`. pub type $radium$(<$t>)? = Cell<$base>; /// Note: the standard library has an unstable atomic for this type. /// `radium` commits to operating on the stable release series, and so /// will not use its atomic variant, but is willing to prepare for /// assumed stabilization by acting on the `Cell`. impl$(<$t>)? Nuclear for $base { type Nucleus = $radium$(<$t>)?; } }; } alias! { "8" => { bool => RadiumBool => AtomicBool; i8 => RadiumI8 => AtomicI8; u8 => RadiumU8 => AtomicU8; } "16" => { i16 => RadiumI16 => AtomicI16; u16 => RadiumU16 => AtomicU16; } "32" => { i32 => RadiumI32 => AtomicI32; u32 => RadiumU32 => AtomicU32; } "64" => { i64 => RadiumI64 => AtomicI64; u64 => RadiumU64 => AtomicU64; } "128" => { i128 => RadiumI128; // => AtomicI128; // when this stabilizes u128 => RadiumU128; // => AtomicU128; // when this stabilizes } "ptr" => { isize => RadiumIsize => AtomicIsize; usize => RadiumUsize => AtomicUsize; @ *mut T => RadiumPtr => AtomicPtr; } } #[cfg(test)] mod tests { use static_assertions::*; use super::*; #[test] fn atom_impls() { #[cfg(target_has_atomic = "8")] { assert_impl_all!(Atom: Debug, Default, From, Send, Sync); assert_impl_all!(Atom: Debug, Default, From, Send, Sync); assert_impl_all!(Atom: Debug, Default, From, Send, Sync); } #[cfg(target_has_atomic = "16")] { assert_impl_all!(Atom: Debug, Default, From, Send, Sync); assert_impl_all!(Atom: Debug, Default, From, Send, Sync); } #[cfg(target_has_atomic = "32")] { assert_impl_all!(Atom: Debug, Default, From, Send, Sync); assert_impl_all!(Atom: Debug, Default, From, Send, Sync); } #[cfg(target_has_atomic = "64")] { assert_impl_all!(Atom: Debug, Default, From, Send, Sync); assert_impl_all!(Atom: Debug, Default, From, Send, Sync); } #[cfg(target_has_atomic = "ptr")] { assert_impl_all!(Atom: Debug, Default, From, Send, Sync); assert_impl_all!(Atom: Debug, Default, From, Send, Sync); assert_impl_all!(Atom<*mut ()>: Debug, From<*mut ()>, Send, Sync); } } #[test] fn isotope_impls() { assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope: Debug, Default, From); assert_impl_all!(Isotope<*mut ()>: Debug, From<*mut ()>); } #[test] fn radon_impls() { assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon: Debug, Default, From); assert_impl_all!(Radon<*mut ()>: Debug, From<*mut ()>); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon: Sync); assert_not_impl_any!(Radon<*mut ()>: Sync); } #[test] fn isotope_atomic() { cfg_if::cfg_if! { if #[cfg(target_has_atomic = "8")] { assert_impl_all!(Isotope: Sync); assert_impl_all!(Isotope: Sync); assert_impl_all!(Isotope: Sync); } else { assert_not_impl_any!(Isotope: Sync); assert_not_impl_any!(Isotope: Sync); assert_not_impl_any!(Isotope: Sync); } } cfg_if::cfg_if! { if #[cfg(target_has_atomic = "16")] { assert_impl_all!(Isotope: Sync); assert_impl_all!(Isotope: Sync); } else { assert_not_impl_any!(Isotope: Sync); assert_not_impl_any!(Isotope: Sync); } } cfg_if::cfg_if! { if #[cfg(target_has_atomic = "32")] { assert_impl_all!(Isotope: Sync); assert_impl_all!(Isotope: Sync); } else { assert_not_impl_any!(Isotope: Sync); assert_not_impl_any!(Isotope: Sync); } } cfg_if::cfg_if! { if #[cfg(target_has_atomic = "64")] { assert_impl_all!(Isotope: Sync); assert_impl_all!(Isotope: Sync); } else { assert_not_impl_any!(Isotope: Sync); assert_not_impl_any!(Isotope: Sync); } } cfg_if::cfg_if! { if #[cfg(target_has_atomic = "ptr")] { assert_impl_all!(Isotope: Sync); assert_impl_all!(Isotope: Sync); assert_impl_all!(Isotope<*mut ()>: Sync); } else { assert_not_impl_any!(Isotope: Sync); assert_not_impl_any!(Isotope: Sync); assert_not_impl_any!(Isotope<*mut ()>: Sync); } } // These are always non-atomic until `Atomic*128` stabilizes. assert_not_impl_any!(Isotope: Sync); assert_not_impl_any!(Isotope: Sync); } }