add new_cyclic_in for Arc
This commit is contained in:
parent
2383cc9910
commit
68169d3103
@ -1322,6 +1322,99 @@ pub unsafe fn assume_init(self) -> Arc<[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.
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user