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.