Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make AtomicCell::is_lock_free always const fn #600

Merged
merged 1 commit into from Nov 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions crossbeam-utils/Cargo.toml
Expand Up @@ -31,12 +31,10 @@ nightly = []

[dependencies]
cfg-if = "1"
const_fn = "0.4"
lazy_static = { version = "1.4.0", optional = true }

[build-dependencies]
autocfg = "1.0.0"

[dev-dependencies]
rand = "0.7.3"
rustversion = "1.0"
22 changes: 16 additions & 6 deletions crossbeam-utils/src/atomic/atomic_cell.rs
Expand Up @@ -12,7 +12,6 @@ use core::sync::atomic::{self, AtomicBool, Ordering};
use std::panic::{RefUnwindSafe, UnwindSafe};

use super::seq_lock::SeqLock;
use const_fn::const_fn;

/// A thread-safe mutable memory location.
///
Expand Down Expand Up @@ -106,7 +105,6 @@ impl<T> AtomicCell<T> {
/// // operations on them will have to use global locks for synchronization.
/// assert_eq!(AtomicCell::<[u8; 1000]>::is_lock_free(), false);
/// ```
#[const_fn("1.46")]
pub const fn is_lock_free() -> bool {
atomic_is_lock_free::<T>()
}
Expand Down Expand Up @@ -611,10 +609,9 @@ impl<T: Copy + fmt::Debug> fmt::Debug for AtomicCell<T> {
}

/// Returns `true` if values of type `A` can be transmuted into values of type `B`.
#[const_fn("1.46")]
const fn can_transmute<A, B>() -> bool {
// Sizes must be equal, but alignment of `A` must be greater or equal than that of `B`.
mem::size_of::<A>() == mem::size_of::<B>() && mem::align_of::<A>() >= mem::align_of::<B>()
(mem::size_of::<A>() == mem::size_of::<B>()) & (mem::align_of::<A>() >= mem::align_of::<B>())
}

/// Returns a reference to the global lock associated with the `AtomicCell` at address `addr`.
Expand Down Expand Up @@ -810,16 +807,29 @@ macro_rules! atomic {
atomic!(@check, $t, atomic::AtomicU32, $a, $atomic_op);
#[cfg(has_atomic_u64)]
atomic!(@check, $t, atomic::AtomicU64, $a, $atomic_op);
#[cfg(has_atomic_u128)]
atomic!(@check, $t, atomic::AtomicU128, $a, $atomic_op);

break $fallback_op;
}
};
}

/// Returns `true` if operations on `AtomicCell<T>` are lock-free.
#[const_fn("1.46")]
const fn atomic_is_lock_free<T>() -> bool {
atomic! { T, _a, true, false }
// HACK(taiki-e): This is equivalent to `atomic! { T, _a, true, false }`, but can be used in const fn even in Rust 1.36.
let is_lock_free = can_transmute::<T, AtomicUnit>() | can_transmute::<T, atomic::AtomicUsize>();
#[cfg(has_atomic_u8)]
let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU8>();
#[cfg(has_atomic_u16)]
let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU16>();
#[cfg(has_atomic_u32)]
let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU32>();
#[cfg(has_atomic_u64)]
let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU64>();
#[cfg(has_atomic_u128)]
let is_lock_free = is_lock_free | can_transmute::<T, atomic::AtomicU128>();
is_lock_free
}

/// Atomically reads data from `src`.
Expand Down
5 changes: 2 additions & 3 deletions crossbeam-utils/tests/atomic_cell.rs
Expand Up @@ -22,11 +22,10 @@ fn is_lock_free() {
assert_eq!(AtomicCell::<u128>::is_lock_free(), cfg!(has_atomic_u128));
}

#[rustversion::since(1.46)]
#[test]
fn const_is_lock_free() {
const _: bool = AtomicCell::<usize>::is_lock_free();
const _: bool = AtomicCell::<isize>::is_lock_free();
const _U: bool = AtomicCell::<usize>::is_lock_free();
const _I: bool = AtomicCell::<isize>::is_lock_free();
}

#[test]
Expand Down