Skip to content

Commit

Permalink
remove the Clone requirement
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew Giordano committed Sep 6, 2024
1 parent 24eca28 commit 7b61eea
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 173 deletions.
158 changes: 78 additions & 80 deletions alloc/src/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,84 @@ impl<T, A: Allocator> Rc<T, A> {
}
}

/// Constructs a new `Rc<T, A>` in the given allocator while giving you a `Weak<T, A>` to the allocation,
/// to allow you to construct a `T` which holds a weak pointer to itself.
///
/// Generally, a structure circularly referencing itself, either directly or
/// indirectly, should not hold a strong reference to itself to prevent a memory leak.
/// Using this function, you get access to the weak pointer during the
/// initialization of `T`, before the `Rc<T, A>` is created, such that you can
/// clone and store it inside the `T`.
///
/// `new_cyclic_in` first allocates the managed allocation for the `Rc<T, A>`,
/// then calls your closure, giving it a `Weak<T, A>` to this allocation,
/// and only afterwards completes the construction of the `Rc<T, A>` by placing
/// the `T` returned from your closure into the allocation.
///
/// Since the new `Rc<T, A>` is not fully-constructed until `Rc<T, A>::new_cyclic_in`
/// returns, calling [`upgrade`] on the weak reference inside your closure will
/// fail and result in a `None` value.
///
/// # Panics
///
/// If `data_fn` panics, the panic is propagated to the caller, and the
/// temporary [`Weak<T, A>`] is dropped normally.
///
/// # Examples
///
/// See [`new_cyclic`].
///
/// [`new_cyclic`]: Rc::new_cyclic
/// [`upgrade`]: Weak::upgrade
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "allocator_api", issue = "32838")]
pub fn new_cyclic_in<F>(data_fn: F, alloc: A) -> Rc<T, A>
where
F: FnOnce(&Weak<T, A>) -> T,
{
// Note: comments and implementation are copied from Rc::new_cyclic.

// Construct the inner in the "uninitialized" state with a single
// weak reference.
let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in(
RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: mem::MaybeUninit::<T>::uninit(),
},
alloc,
));
let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into();
let init_ptr: NonNull<RcBox<T>> = uninit_ptr.cast();

let weak = Weak { ptr: init_ptr, alloc: alloc };

// It's important we don't give up ownership of the weak pointer, or
// else the memory might be freed by the time `data_fn` returns. If
// we really wanted to pass ownership, we could create an additional
// weak pointer for ourselves, but this would result in additional
// updates to the weak reference count which might not be necessary
// otherwise.
let data = data_fn(&weak);

let strong = unsafe {
let inner = init_ptr.as_ptr();
ptr::write(ptr::addr_of_mut!((*inner).value), data);

let prev_value = (*inner).strong.get();
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");
(*inner).strong.set(1);

// Strong references should collectively own a shared weak reference,
// so don't run the destructor for our old weak reference.
let alloc = weak.into_raw_with_allocator().1;

Rc::from_inner_in(init_ptr, alloc)
};

strong
}

/// Constructs a new `Rc<T>` in the provided allocator, returning an error if the allocation
/// fails
///
Expand Down Expand Up @@ -2264,86 +2342,6 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Rc<T, A> {
}
}

impl<T, A: Allocator + Clone> Rc<T, A> {
/// Constructs a new `Rc<T, A>` in the given allocator while giving you a `Weak<T, A>` to the allocation,
/// to allow you to construct a `T` which holds a weak pointer to itself.
///
/// Generally, a structure circularly referencing itself, either directly or
/// indirectly, should not hold a strong reference to itself to prevent a memory leak.
/// Using this function, you get access to the weak pointer during the
/// initialization of `T`, before the `Rc<T, A>` is created, such that you can
/// clone and store it inside the `T`.
///
/// `new_cyclic` first allocates the managed allocation for the `Rc<T, A>`,
/// then calls your closure, giving it a `Weak<T, A>` to this allocation,
/// and only afterwards completes the construction of the `Rc<T, A>` by placing
/// the `T` returned from your closure into the allocation.
///
/// Since the new `Rc<T, A>` is not fully-constructed until `Rc<T, A>::new_cyclic_in`
/// returns, calling [`upgrade`] on the weak reference inside your closure will
/// fail and result in a `None` value.
///
/// # Panics
///
/// If `data_fn` panics, the panic is propagated to the caller, and the
/// temporary [`Weak<T, A>`] is dropped normally.
///
/// # Examples
///
/// See [`new_cyclic`].
///
/// [`new_cyclic`]: Rc::new_cyclic
/// [`upgrade`]: Weak::upgrade
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "allocator_api", issue = "32838")]
pub fn new_cyclic_in<F>(data_fn: F, alloc: A) -> Rc<T, A>
where
F: FnOnce(&Weak<T, A>) -> T,
{
// Note: comments and implementation are copied from Rc::new_cyclic.

// Construct the inner in the "uninitialized" state with a single
// weak reference.
let uninit_ptr: NonNull<_> = Box::leak(Box::new_in(
RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: mem::MaybeUninit::<T>::uninit(),
},
alloc.clone(),
))
.into();

let init_ptr: NonNull<RcBox<T>> = uninit_ptr.cast();

let weak = Weak { ptr: init_ptr, alloc: alloc.clone() };

// It's important we don't give up ownership of the weak pointer, or
// else the memory might be freed by the time `data_fn` returns. If
// we really wanted to pass ownership, we could create an additional
// weak pointer for ourselves, but this would result in additional
// updates to the weak reference count which might not be necessary
// otherwise.
let data = data_fn(&weak);

let strong = unsafe {
let inner = init_ptr.as_ptr();
ptr::write(ptr::addr_of_mut!((*inner).value), data);

let prev_value = (*inner).strong.get();
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");
(*inner).strong.set(1);

Rc::from_inner_in(init_ptr, alloc)
};

// Strong references should collectively own a shared weak reference,
// so don't run the destructor for our old weak reference.
mem::forget(weak);
strong
}
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Default> Default for Rc<T> {
Expand Down
185 changes: 92 additions & 93 deletions alloc/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,98 @@ impl<T, A: Allocator> Arc<T, A> {
}
}

/// Constructs a new `Arc<T, A>` in the given allocator while giving you a `Weak<T, A>` to the allocation,
/// to allow you to construct a `T` which holds a weak pointer to itself.
///
/// Generally, a structure circularly referencing itself, either directly or
/// indirectly, should not hold a strong reference to itself to prevent a memory leak.
/// Using this function, you get access to the weak pointer during the
/// initialization of `T`, before the `Arc<T, A>` is created, such that you can
/// clone and store it inside the `T`.
///
/// `new_cyclic_in` first allocates the managed allocation for the `Arc<T, A>`,
/// then calls your closure, giving it a `Weak<T, A>` to this allocation,
/// and only afterwards completes the construction of the `Arc<T, A>` by placing
/// the `T` returned from your closure into the allocation.
///
/// Since the new `Arc<T, A>` is not fully-constructed until `Arc<T, A>::new_cyclic_in`
/// returns, calling [`upgrade`] on the weak reference inside your closure will
/// fail and result in a `None` value.
///
/// # Panics
///
/// If `data_fn` panics, the panic is propagated to the caller, and the
/// temporary [`Weak<T>`] is dropped normally.
///
/// # Example
///
/// See [`new_cyclic`]
///
/// [`new_cyclic`]: Arc::new_cyclic
/// [`upgrade`]: Weak::upgrade
#[cfg(not(no_global_oom_handling))]
#[inline]
#[stable(feature = "arc_new_cyclic", since = "1.60.0")]
pub fn new_cyclic_in<F>(data_fn: F, alloc: A) -> Arc<T, A>
where
F: FnOnce(&Weak<T, A>) -> T,
{
// Note: these comments and much of the implementation is copied from Arc::new_cyclic.

// Construct the inner in the "uninitialized" state with a single
// weak reference.
let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in(
ArcInner {
strong: atomic::AtomicUsize::new(0),
weak: atomic::AtomicUsize::new(1),
data: mem::MaybeUninit::<T>::uninit(),
},
alloc,
));
let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into();
let init_ptr: NonNull<ArcInner<T>> = uninit_ptr.cast();

let weak = Weak { ptr: init_ptr, alloc: alloc };

// It's important we don't give up ownership of the weak pointer, or
// else the memory might be freed by the time `data_fn` returns. If
// we really wanted to pass ownership, we could create an additional
// weak pointer for ourselves, but this would result in additional
// updates to the weak reference count which might not be necessary
// otherwise.
let data = data_fn(&weak);

// Now we can properly initialize the inner value and turn our weak
// reference into a strong reference.
let strong = unsafe {
let inner = init_ptr.as_ptr();
ptr::write(ptr::addr_of_mut!((*inner).data), data);

// The above write to the data field must be visible to any threads which
// observe a non-zero strong count. Therefore we need at least "Release" ordering
// in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`.
//
// "Acquire" ordering is not required. When considering the possible behaviours
// of `data_fn` we only need to look at what it could do with a reference to a
// non-upgradeable `Weak`:
// - It can *clone* the `Weak`, increasing the weak reference count.
// - It can drop those clones, decreasing the weak reference count (but never to zero).
//
// These side effects do not impact us in any way, and no other side effects are
// possible with safe code alone.
let prev_value = (*inner).strong.fetch_add(1, Release);
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");

// Strong references should collectively own a shared weak reference,
// so don't run the destructor for our old weak reference.
let alloc = weak.into_raw_with_allocator().1;

Arc::from_inner_in(init_ptr, alloc)
};

strong
}

/// Constructs a new `Pin<Arc<T, A>>` in the provided allocator. If `T` does not implement `Unpin`,
/// then `data` will be pinned in memory and unable to be moved.
#[cfg(not(no_global_oom_handling))]
Expand Down Expand Up @@ -1322,99 +1414,6 @@ impl<T, A: Allocator> Arc<[mem::MaybeUninit<T>], A> {
}
}

impl<T, A: Allocator + Clone> Arc<T, A> {
/// Constructs a new `Arc<T, A>` in the given allocator while giving you a `Weak<T, A>` to the allocation,
/// to allow you to construct a `T` which holds a weak pointer to itself.
///
/// Generally, a structure circularly referencing itself, either directly or
/// indirectly, should not hold a strong reference to itself to prevent a memory leak.
/// Using this function, you get access to the weak pointer during the
/// initialization of `T`, before the `Arc<T, A>` is created, such that you can
/// clone and store it inside the `T`.
///
/// `new_cyclic` first allocates the managed allocation for the `Arc<T, A>`,
/// then calls your closure, giving it a `Weak<T, A>` to this allocation,
/// and only afterwards completes the construction of the `Arc<T, A>` by placing
/// the `T` returned from your closure into the allocation.
///
/// Since the new `Arc<T, A>` is not fully-constructed until `Arc<T, A>::new_cyclic_in`
/// returns, calling [`upgrade`] on the weak reference inside your closure will
/// fail and result in a `None` value.
///
/// # Panics
///
/// If `data_fn` panics, the panic is propagated to the caller, and the
/// temporary [`Weak<T>`] is dropped normally.
///
/// # Example
///
/// See [`new_cyclic`]
///
/// [`new_cyclic`]: Arc::new_cyclic
/// [`upgrade`]: Weak::upgrade
#[cfg(not(no_global_oom_handling))]
#[inline]
#[stable(feature = "arc_new_cyclic", since = "1.60.0")]
pub fn new_cyclic_in<F>(data_fn: F, alloc: A) -> Arc<T, A>
where
F: FnOnce(&Weak<T, A>) -> T,
{
// Note: these comments and much of the implementation is copied from Arc::new_cyclic.

// Construct the inner in the "uninitialized" state with a single
// weak reference.
let uninit_ptr: NonNull<_> = Box::leak(Box::new_in(
ArcInner {
strong: atomic::AtomicUsize::new(0),
weak: atomic::AtomicUsize::new(1),
data: mem::MaybeUninit::<T>::uninit(),
},
alloc.clone(),
))
.into();
let init_ptr: NonNull<ArcInner<T>> = uninit_ptr.cast();

let weak = Weak { ptr: init_ptr, alloc: alloc.clone() };

// It's important we don't give up ownership of the weak pointer, or
// else the memory might be freed by the time `data_fn` returns. If
// we really wanted to pass ownership, we could create an additional
// weak pointer for ourselves, but this would result in additional
// updates to the weak reference count which might not be necessary
// otherwise.
let data = data_fn(&weak);

// Now we can properly initialize the inner value and turn our weak
// reference into a strong reference.
let strong = unsafe {
let inner = init_ptr.as_ptr();
ptr::write(ptr::addr_of_mut!((*inner).data), data);

// The above write to the data field must be visible to any threads which
// observe a non-zero strong count. Therefore we need at least "Release" ordering
// in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`.
//
// "Acquire" ordering is not required. When considering the possible behaviours
// of `data_fn` we only need to look at what it could do with a reference to a
// non-upgradeable `Weak`:
// - It can *clone* the `Weak`, increasing the weak reference count.
// - It can drop those clones, decreasing the weak reference count (but never to zero).
//
// These side effects do not impact us in any way, and no other side effects are
// possible with safe code alone.
let prev_value = (*inner).strong.fetch_add(1, Release);
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");

Arc::from_inner_in(init_ptr, alloc)
};

// Strong references should collectively own a shared weak reference,
// so don't run the destructor for our old weak reference.
mem::forget(weak);
strong
}
}

impl<T: ?Sized> Arc<T> {
/// Constructs an `Arc<T>` from a raw pointer.
///
Expand Down

0 comments on commit 7b61eea

Please sign in to comment.