Ideally, there would even be a fully AtomicCell<T>, for the cases where T can be backed by an AtomicU…. The first naïve idea that comes to mind, is to use the size of T, and "generically" bound it by the size of the biggest AtomicU… available on that platform.
- This can be done in nightly through
const_evaluatable_checked.
Alas, this is not enough: indeed, the semantic of atomic operations are not well defined for uninitialized bytes / padding bytes. This means that a type such as (u8, u16), which fits in an AtomicU32, has the issue that performing an AtomicU32-based read there would be reading the padding byte (wherever it may be), operation that has no defined behavior.
So, we end up down the rabbit hole of FromBytes/AsBytes (on top of size-bounding), e.g., a bunch of unsafe marker traits (ideally, autogenerated, see the safe-transmute working group / effor; else manually implemented).
The whole thing is fuzzy enough not to be implemented in the standard library yet, but I could imagine a library crate such as the unsound AtomicCell of ::crossbeam but with the added addition of putting the burden of safety onto users of the library through such a marker trait:
#![feature(const_generics)]
mod lib {
use ::std::sync::atomic::{*, self};
/// # Safety
/// - it must be sound to transmute back and forth between `Self` and `u32`
pub
unsafe trait U32Compat
:
Copy +
// ::zerocopy::AsBytes +
// ::zerocopy::FromBytes +
{}
unsafe
impl U32Compat for u32 {}
pub use ::core;
pub use ::zerocopy;
#[macro_export]
macro_rules! safe_impl {(
$(@for[$($generics:tt)*])?
$T:ty $(,)?
) => (
// Check the required properties
const _: () = {
fn __checks <T> () where
T : $crate::zerocopy::AsBytes,
T : $crate::zerocopy::FromBytes,
{}
fn __forall<$($($generics)*)?> ()
{
let _ = __checks::<$T>;
let _ = $crate::core::mem::transmute::<$T, $crate::core::primitive::u32>;
}
};
unsafe
impl<$($($generics)*)?> U32Compat for $T {}
)}
fn transmute<Src, Dst> (it: Src)
-> Dst
where
Src : U32Compat,
Dst : U32Compat,
{
unsafe {
// Safety: Src -> u32 -> Dst is safe per `U32Compat` contract
// in practice we just cut the middleman.
::core::mem::transmute_copy(&it)
}
}
#[repr(transparent)]
pub
struct AtomicU32Cell<T : U32Compat>(
AtomicU32,
PhantomData<T>,
);
impl<T : U32Compat> AtomicU32Cell<T> {
fn new (value: T)
-> AtomicU32Cell<T>
{
Self(transmute(value), Default::default())
}
fn load (
self: &'_ AtomicU32Cell<T>,
ordering: atomic::Ordering,
) -> T
{
transmute(self.0.load(ordering))
}
fn store (
self: &'_ AtomicU32Cell<T>,
value: T,
ordering: atomic::Ordering,
)
{
self.0.store(transmute(value), ordering)
}
}
}
and so on for the other atomic sizes.