use core::cell::UnsafeCell;
use core::cmp;
use core::fmt;
-use core::mem;
+use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::sync::atomic::Ordering;
use core::ptr;
/// [`Acquire`]: std::sync::atomic::Ordering::Acquire
/// [`Release`]: std::sync::atomic::Ordering::Release
#[repr(transparent)]
-pub struct AtomicCell<T: ?Sized> {
+pub struct AtomicCell<T> {
/// The inner value.
///
/// If this value can be transmuted into a primitive atomic type, it will be treated as such.
/// Otherwise, all potentially concurrent operations on this data will be protected by a global
/// lock.
- value: UnsafeCell<T>,
+ ///
+ /// Using MaybeUninit to prevent code outside the cell from observing partially initialized state:
+ /// <https://github.com/crossbeam-rs/crossbeam/issues/833>
+ ///
+ /// Note:
+ /// - we'll never store uninitialized `T` due to our API only using initialized `T`.
+ /// - this `MaybeUninit` does *not* fix <https://github.com/crossbeam-rs/crossbeam/issues/315>.
+ value: UnsafeCell<MaybeUninit<T>>,
}
unsafe impl<T: Send> Send for AtomicCell<T> {}
/// ```
pub const fn new(val: T) -> AtomicCell<T> {
AtomicCell {
- value: UnsafeCell::new(val),
+ value: UnsafeCell::new(MaybeUninit::new(val)),
}
}
/// Consumes the atomic and returns the contained value.
///
+ /// This is safe because passing `self` by value guarantees that no other threads are
+ /// concurrently accessing the atomic data.
+ ///
/// # Examples
///
/// ```
/// assert_eq!(v, 7);
/// ```
pub fn into_inner(self) -> T {
- self.value.into_inner()
+ let this = ManuallyDrop::new(self);
+ // SAFETY:
+ // - passing `self` by value guarantees that no other threads are concurrently
+ // accessing the atomic data
+ // - the raw pointer passed in is valid because we got it from an owned value.
+ // - `ManuallyDrop` prevents double dropping `T`
+ unsafe { this.as_ptr().read() }
}
/// Returns `true` if operations on values of this type are lock-free.
drop(self.swap(val));
} else {
unsafe {
- atomic_store(self.value.get(), val);
+ atomic_store(self.as_ptr(), val);
}
}
}
/// assert_eq!(a.load(), 8);
/// ```
pub fn swap(&self, val: T) -> T {
- unsafe { atomic_swap(self.value.get(), val) }
+ unsafe { atomic_swap(self.as_ptr(), val) }
}
-}
-impl<T: ?Sized> AtomicCell<T> {
/// Returns a raw pointer to the underlying data in this atomic cell.
///
/// # Examples
/// ```
#[inline]
pub fn as_ptr(&self) -> *mut T {
- self.value.get()
+ self.value.get() as *mut T
}
}
/// assert_eq!(a.load(), 7);
/// ```
pub fn load(&self) -> T {
- unsafe { atomic_load(self.value.get()) }
+ unsafe { atomic_load(self.as_ptr()) }
}
}
/// assert_eq!(a.load(), 2);
/// ```
pub fn compare_exchange(&self, current: T, new: T) -> Result<T, T> {
- unsafe { atomic_compare_exchange_weak(self.value.get(), current, new) }
+ unsafe { atomic_compare_exchange_weak(self.as_ptr(), current, new) }
}
/// Fetches the value, and applies a function to it that returns an optional
}
}
+// `MaybeUninit` prevents `T` from being dropped, so we need to implement `Drop`
+// for `AtomicCell` to avoid leaks of non-`Copy` types.
+impl<T> Drop for AtomicCell<T> {
+ fn drop(&mut self) {
+ if mem::needs_drop::<T>() {
+ // SAFETY:
+ // - the mutable reference guarantees that no other threads are concurrently accessing the atomic data
+ // - the raw pointer passed in is valid because we got it from a reference
+ // - `MaybeUninit` prevents double dropping `T`
+ unsafe {
+ self.as_ptr().drop_in_place();
+ }
+ }
+ }
+}
+
macro_rules! impl_arithmetic {
($t:ty, fallback, $example:tt) => {
impl AtomicCell<$t> {
/// ```
#[inline]
pub fn fetch_add(&self, val: $t) -> $t {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = value.wrapping_add(val);
old
/// ```
#[inline]
pub fn fetch_sub(&self, val: $t) -> $t {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = value.wrapping_sub(val);
old
/// ```
#[inline]
pub fn fetch_and(&self, val: $t) -> $t {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value &= val;
old
/// ```
#[inline]
pub fn fetch_nand(&self, val: $t) -> $t {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = !(old & val);
old
/// ```
#[inline]
pub fn fetch_or(&self, val: $t) -> $t {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value |= val;
old
/// ```
#[inline]
pub fn fetch_xor(&self, val: $t) -> $t {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value ^= val;
old
/// ```
#[inline]
pub fn fetch_max(&self, val: $t) -> $t {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = cmp::max(old, val);
old
/// ```
#[inline]
pub fn fetch_min(&self, val: $t) -> $t {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = cmp::min(old, val);
old
#[inline]
pub fn fetch_add(&self, val: $t) -> $t {
if can_transmute::<$t, $atomic>() {
- let a = unsafe { &*(self.value.get() as *const $atomic) };
+ let a = unsafe { &*(self.as_ptr() as *const $atomic) };
a.fetch_add(val, Ordering::AcqRel)
} else {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = value.wrapping_add(val);
old
#[inline]
pub fn fetch_sub(&self, val: $t) -> $t {
if can_transmute::<$t, $atomic>() {
- let a = unsafe { &*(self.value.get() as *const $atomic) };
+ let a = unsafe { &*(self.as_ptr() as *const $atomic) };
a.fetch_sub(val, Ordering::AcqRel)
} else {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = value.wrapping_sub(val);
old
#[inline]
pub fn fetch_and(&self, val: $t) -> $t {
if can_transmute::<$t, $atomic>() {
- let a = unsafe { &*(self.value.get() as *const $atomic) };
+ let a = unsafe { &*(self.as_ptr() as *const $atomic) };
a.fetch_and(val, Ordering::AcqRel)
} else {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value &= val;
old
#[inline]
pub fn fetch_nand(&self, val: $t) -> $t {
if can_transmute::<$t, $atomic>() {
- let a = unsafe { &*(self.value.get() as *const $atomic) };
+ let a = unsafe { &*(self.as_ptr() as *const $atomic) };
a.fetch_nand(val, Ordering::AcqRel)
} else {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = !(old & val);
old
#[inline]
pub fn fetch_or(&self, val: $t) -> $t {
if can_transmute::<$t, $atomic>() {
- let a = unsafe { &*(self.value.get() as *const $atomic) };
+ let a = unsafe { &*(self.as_ptr() as *const $atomic) };
a.fetch_or(val, Ordering::AcqRel)
} else {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value |= val;
old
#[inline]
pub fn fetch_xor(&self, val: $t) -> $t {
if can_transmute::<$t, $atomic>() {
- let a = unsafe { &*(self.value.get() as *const $atomic) };
+ let a = unsafe { &*(self.as_ptr() as *const $atomic) };
a.fetch_xor(val, Ordering::AcqRel)
} else {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value ^= val;
old
// TODO: Atomic*::fetch_max requires Rust 1.45.
self.fetch_update(|old| Some(cmp::max(old, val))).unwrap()
} else {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = cmp::max(old, val);
old
// TODO: Atomic*::fetch_min requires Rust 1.45.
self.fetch_update(|old| Some(cmp::min(old, val))).unwrap()
} else {
- let _guard = lock(self.value.get() as usize).write();
- let value = unsafe { &mut *(self.value.get()) };
+ let _guard = lock(self.as_ptr() as usize).write();
+ let value = unsafe { &mut *(self.as_ptr()) };
let old = *value;
*value = cmp::min(old, val);
old
/// ```
#[inline]
pub fn fetch_and(&self, val: bool) -> bool {
- let a = unsafe { &*(self.value.get() as *const AtomicBool) };
+ let a = unsafe { &*(self.as_ptr() as *const AtomicBool) };
a.fetch_and(val, Ordering::AcqRel)
}
/// ```
#[inline]
pub fn fetch_nand(&self, val: bool) -> bool {
- let a = unsafe { &*(self.value.get() as *const AtomicBool) };
+ let a = unsafe { &*(self.as_ptr() as *const AtomicBool) };
a.fetch_nand(val, Ordering::AcqRel)
}
/// ```
#[inline]
pub fn fetch_or(&self, val: bool) -> bool {
- let a = unsafe { &*(self.value.get() as *const AtomicBool) };
+ let a = unsafe { &*(self.as_ptr() as *const AtomicBool) };
a.fetch_or(val, Ordering::AcqRel)
}
/// ```
#[inline]
pub fn fetch_xor(&self, val: bool) -> bool {
- let a = unsafe { &*(self.value.get() as *const AtomicBool) };
+ let a = unsafe { &*(self.as_ptr() as *const AtomicBool) };
a.fetch_xor(val, Ordering::AcqRel)
}
}