Skip to content

Commit

Permalink
impl Drop for AtomicCell
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed May 22, 2022
1 parent b947740 commit 79c4ae5
Showing 1 changed file with 31 additions and 3 deletions.
34 changes: 31 additions & 3 deletions crossbeam-utils/src/atomic/atomic_cell.rs
Expand Up @@ -5,7 +5,7 @@ use crate::primitive::sync::atomic::{self, AtomicBool};
use core::cell::UnsafeCell;
use core::cmp;
use core::fmt;
use core::mem::{self, MaybeUninit};
use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::sync::atomic::Ordering;

use core::ptr;
Expand Down Expand Up @@ -39,6 +39,10 @@ pub struct AtomicCell<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>>,
}

Expand Down Expand Up @@ -68,6 +72,9 @@ impl<T> AtomicCell<T> {

/// 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
///
/// ```
Expand All @@ -79,8 +86,13 @@ impl<T> AtomicCell<T> {
/// assert_eq!(v, 7);
/// ```
pub fn into_inner(self) -> T {
// SAFETY: we'll never store uninitialized `T`
unsafe { self.value.into_inner().assume_init() }
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.
Expand Down Expand Up @@ -294,6 +306,22 @@ impl<T: Copy + Eq> AtomicCell<T> {
}
}

// `MaybeUninit` prevents `T` from being dropped, 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 {
drop(self.as_ptr().read());
}
}
}
}

macro_rules! impl_arithmetic {
($t:ty, fallback, $example:tt) => {
impl AtomicCell<$t> {
Expand Down

0 comments on commit 79c4ae5

Please sign in to comment.