Crate radium

source ·
Expand description

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<T> wraps the standard library atomics, and only accepts T parameters where the target has an AtomicT type; Isotope<T> accepts any of the types which could be atomic, and wraps atomics where they exist and silently decays to Cell<T> where they do not; and Radon<T> wraps Cell<T>. All three of these types have no API except for implementing Radium, Debug, Default, and From<T>, 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<T> 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 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 - https://twitter.com/kneecaw/status/1132695060812849154

Feelin’ lazy: Has someone already written a helper trait abstracting operations over AtomicUsize and Cell<usize> for generic code which may not care about atomicity?

@ManishEarth - https://twitter.com/ManishEarth/status/1132706585300496384

no but call the crate radium

(since people didn’t care that it was radioactive and used it in everything)

Re-exports

  • pub use crate::types::Atom;
  • pub use crate::types::Isotope;
  • pub use crate::types::Radon;

Modules

  • Marker traits used to gate Radium methods and newtype parameters.
  • Aliases and Type Families

Traits

  • Unified Shared-Mutable API