-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Properly gate atomics implementation
Signed-off-by: Joe Richey <[email protected]>
- Loading branch information
Showing
3 changed files
with
195 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
//! Conditionally implement traits for atomic types | ||
//! | ||
//! Note that we only implement traits for atomics types if the corresponding | ||
//! [`#[cfg(target_has_atomic)]`](https://doc.rust-lang.org/reference/conditional-compilation.html#target_has_atomic) | ||
//! configuration option is true. | ||
//! | ||
//! Note: On targets like `thumbv6m-none-eabi` (which has an [`AtomicU32`] | ||
//! type but `#[cfg(target_has_atomic = "32")]` is false), we don't implement | ||
//! the zerocopy traits. We will be able to handle this when Rust stabilizes | ||
//! [`#[cfg(target_has_atomic_load_store)]`](https://www.github.com/rust-lang/rust/issues/94039). | ||
|
||
use core::{cell::UnsafeCell, sync::atomic::*}; | ||
|
||
use crate::{ | ||
pointer::{invariant::Invariants, Maybe}, | ||
util::{Covariant, Invariant, TransparentWrapper}, | ||
FromBytes, FromZeros, IntoBytes, KnownLayout, TryFromBytes, Unaligned, | ||
}; | ||
|
||
/// Implements `TransparentWrapper` for an atomic type. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The caller promises that `$atomic` is an atomic type whose native equivalent | ||
/// is `$native`. | ||
macro_rules! unsafe_impl_transparent_wrapper_for_atomic { | ||
($atomic:ty [$native:ty]) => { | ||
// We implement for `$atomic` and set `Inner = $native`. The caller has | ||
// promised that `$atomic` and `$native` are an atomic type and its | ||
// native counterpart, respectively. Per [1], `$atomic` and `$native` | ||
// have the same size. | ||
// | ||
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): | ||
// Cite docs once they've landed. | ||
type Inner = UnsafeCell<$native>; | ||
|
||
// SAFETY: It is "obvious" that each atomic type contains a single | ||
// `UnsafeCell` that covers all bytes of the type, but we can also prove | ||
// it: | ||
// - Since `$atomic` provides an API which permits loading and storing | ||
// values of type `$native` via a `&self` (shared) reference, *some* | ||
// interior mutation must be happening, and interior mutation can only | ||
// happen via `UnsafeCell`. Further, there must be enough bytes in | ||
// `$atomic` covered by an `UnsafeCell` to hold every possible value | ||
// of `$native`. | ||
// - Per [1], `$atomic` has the same size as `$native`. This on its own | ||
// isn't enough: it would still be possible for `$atomic` to store | ||
// `$native` using a compact representation (for `$native` types for | ||
// which some bit patterns are illegal). However, this is ruled out by | ||
// the fact that `$atomic` has the same bit validity as `$native` [1]. | ||
// Thus, we can conclude that every byte of `$atomic` must be covered | ||
// by an `UnsafeCell`. | ||
// | ||
// Thus, every byte of `$atomic` is covered by an `UnsafeCell`, and we | ||
// set `type Inner = UnsafeCell<$native>`. Thus, `Self` and | ||
// `Self::Inner` have `UnsafeCell`s covering the same byte ranges. | ||
// | ||
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): | ||
// Cite docs once they've landed. | ||
type UnsafeCellVariance = Covariant; | ||
|
||
// SAFETY: No safety justification is required for an invariant | ||
// variance. | ||
type AlignmentVariance = Invariant; | ||
|
||
// SAFETY: Per [1], all atomic types have the same bit validity as their | ||
// native counterparts. The caller has promised that `$atomic` and | ||
// `$native` are an atomic type and its native counterpart, | ||
// respectively. | ||
// | ||
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): | ||
// Cite docs once they've landed. | ||
type ValidityVariance = Covariant; | ||
|
||
fn cast_into_inner(ptr: *mut $atomic) -> *mut UnsafeCell<$native> { | ||
// SAFETY: Per [1] (from comment on impl block), `$atomic` has the | ||
// same size as `$native`. Thus, this cast preserves size. | ||
// | ||
// This cast trivially preserves provenance. | ||
ptr.cast::<UnsafeCell<$native>>() | ||
} | ||
|
||
fn cast_from_inner(ptr: *mut UnsafeCell<$native>) -> *mut $atomic { | ||
// SAFETY: Per [1] (from comment on impl block), `$atomic` has the | ||
// same size as `$native`. Thus, this cast preserves size. | ||
// | ||
// This cast trivially preserves provenance. | ||
ptr.cast::<$atomic>() | ||
} | ||
}; | ||
} | ||
|
||
macro_rules! impl_from_bytes_for_atomic { | ||
(AtomicBool [$native:ty]) => {}; | ||
($atomic:ty [$native:ty]) => { | ||
impl_for_transparent_wrapper!(FromBytes for $atomic [UnsafeCell<$native>]); | ||
}; | ||
} | ||
|
||
/// Implements the zerocopy traits for atomic integer types. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The caller promises that `$atomic` is an atomic type whose native equivalent | ||
/// is `$native`. | ||
macro_rules! impl_traits_for_atomics { | ||
($($atomic:ident [$native:ty]),*) => {$( | ||
/// SAFETY: All of these pass an atomic type and that type's native equivalent | ||
unsafe impl<I: Invariants> TransparentWrapper<I> for $atomic { | ||
unsafe_impl_transparent_wrapper_for_atomic!($atomic [$native]); | ||
} | ||
impl_for_transparent_wrapper!(TryFromBytes for $atomic [UnsafeCell<$native>]); | ||
impl_for_transparent_wrapper!(FromZeros for $atomic [UnsafeCell<$native>]); | ||
impl_from_bytes_for_atomic!($atomic [$native]); | ||
impl_for_transparent_wrapper!(IntoBytes for $atomic [UnsafeCell<$native>]); | ||
|
||
impl_known_layout!($atomic); | ||
)*}; | ||
} | ||
|
||
#[cfg(target_has_atomic = "8")] | ||
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "8")))] | ||
mod atomic_8 { | ||
use super::*; | ||
impl_traits_for_atomics!(AtomicBool[bool], AtomicU8[u8], AtomicI8[i8]); | ||
|
||
safety_comment! { | ||
/// SAFETY: | ||
/// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same size as | ||
/// `bool`, `u8`, and `i8` respectively. Since a type's alignment cannot be | ||
/// smaller than 1 [2], and since its alignment cannot be greater than its | ||
/// size [3], the only possible value for the alignment is 1. Thus, it is | ||
/// sound to implement `Unaligned`. | ||
/// | ||
/// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): | ||
/// Cite docs once they've landed. | ||
/// | ||
/// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: | ||
/// | ||
/// Alignment is measured in bytes, and must be at least 1. | ||
/// | ||
/// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: | ||
/// | ||
/// The size of a value is always a multiple of its alignment. | ||
unsafe_impl!(AtomicBool: Unaligned); | ||
unsafe_impl!(AtomicU8: Unaligned); | ||
unsafe_impl!(AtomicI8: Unaligned); | ||
} | ||
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8); | ||
} | ||
|
||
#[cfg(target_has_atomic = "16")] | ||
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "16")))] | ||
mod atomic_16 { | ||
use super::*; | ||
impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]); | ||
} | ||
|
||
#[cfg(target_has_atomic = "32")] | ||
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "32")))] | ||
mod atomic_32 { | ||
use super::*; | ||
impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]); | ||
} | ||
|
||
#[cfg(target_has_atomic = "64")] | ||
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "64")))] | ||
mod atomic_64 { | ||
use super::*; | ||
impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]); | ||
} | ||
|
||
#[cfg(target_has_atomic = "ptr")] | ||
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "ptr")))] | ||
mod atomic_ptr { | ||
use super::*; | ||
impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]); | ||
|
||
/// SAFETY: All of these pass an atomic type and that type's native equivalent | ||
/// For thin pointers (note that `T: Sized`) AtomicPtr<T> is garunteed to | ||
/// wrap a *mut T. Just like *mut T, we don't implement FromBytes/IntoBytes. | ||
/// | ||
/// See the comments on the *mut T impls for more information. | ||
/// | ||
/// This macro is just used to keep the impl attributes consistent. | ||
unsafe impl<T, I: Invariants> TransparentWrapper<I> for AtomicPtr<T> { | ||
unsafe_impl_transparent_wrapper_for_atomic!(AtomicPtr<T> [*mut T]); | ||
} | ||
impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr<T> [UnsafeCell<*mut T>]); | ||
impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr<T> [UnsafeCell<*mut T>]); | ||
impl_known_layout!(T => AtomicPtr<T>); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.