Skip to content

Commit

Permalink
Implement UniqueArc
Browse files Browse the repository at this point in the history
  • Loading branch information
frank-king committed Nov 30, 2024
1 parent ee612c4 commit 5142171
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 2 deletions.
182 changes: 181 additions & 1 deletion library/alloc/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use core::intrinsics::abort;
use core::iter;
use core::marker::{PhantomData, Unsize};
use core::mem::{self, ManuallyDrop, align_of_val_raw};
use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, LegacyReceiver};
use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver};
use core::panic::{RefUnwindSafe, UnwindSafe};
use core::pin::{Pin, PinCoerceUnsized};
use core::ptr::{self, NonNull};
Expand Down Expand Up @@ -4012,3 +4012,183 @@ impl<T: core::error::Error + ?Sized> core::error::Error for Arc<T> {
core::error::Error::provide(&**self, req);
}
}

/// A uniquely owned [`Arc`].
///
/// This represents an `Arc` that is known to be uniquely owned -- that is, have exactly one strong
/// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong
/// references will fail unless the `UniqueArc` they point to has been converted into a regular `Arc`.
///
/// Because they are uniquely owned, the contents of a `UniqueArc` can be freely mutated. A common
/// use case is to have an object be mutable during its initialization phase but then have it become
/// immutable and converted to a normal `Arc`.
///
/// This can be used as a flexible way to create cyclic data structures, as in the example below.
///
/// ```
/// #![feature(unique_rc_arc)]
/// use std::sync::{Arc, Weak, UniqueArc};
///
/// struct Gadget {
/// #[allow(dead_code)]
/// me: Weak<Gadget>,
/// }
///
/// fn create_gadget() -> Option<Arc<Gadget>> {
/// let mut rc = UniqueArc::new(Gadget {
/// me: Weak::new(),
/// });
/// rc.me = UniqueArc::downgrade(&rc);
/// Some(UniqueArc::into_arc(rc))
/// }
///
/// create_gadget().unwrap();
/// ```
///
/// An advantage of using `UniqueArc` over [`Arc::new_cyclic`] to build cyclic data structures is that
/// [`Arc::new_cyclic`]'s `data_fn` parameter cannot be async or return a [`Result`]. As shown in the
/// previous example, `UniqueArc` allows for more flexibility in the construction of cyclic data,
/// including fallible or async constructors.
#[unstable(feature = "unique_rc_arc", issue = "112566")]
#[derive(Debug)]
pub struct UniqueArc<
T: ?Sized,
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
> {
ptr: NonNull<ArcInner<T>>,
phantom: PhantomData<ArcInner<T>>,
alloc: A,
}

#[unstable(feature = "unique_rc_arc", issue = "112566")]
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<UniqueArc<U, A>>
for UniqueArc<T, A>
{
}

// Depends on A = Global
impl<T> UniqueArc<T> {
/// Creates a new `UniqueArc`.
///
/// Weak references to this `UniqueArc` can be created with [`UniqueArc::downgrade`]. Upgrading
/// these weak references will fail before the `UniqueArc` has been converted into an [`Arc`].
/// After converting the `UniqueArc` into an [`Arc`], any weak references created beforehand will
/// point to the new [`Arc`].
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "unique_rc_arc", issue = "112566")]
pub fn new(value: T) -> Self {
Self::new_in(value, Global)
}
}

impl<T, A: Allocator> UniqueArc<T, A> {
/// Creates a new `UniqueArc` in the provided allocator.
///
/// Weak references to this `UniqueArc` can be created with [`UniqueArc::downgrade`]. Upgrading
/// these weak references will fail before the `UniqueArc` has been converted into an [`Arc`].
/// After converting the `UniqueArc` into an [`Arc`], any weak references created beforehand will
/// point to the new [`Arc`].
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "unique_rc_arc", issue = "112566")]
pub fn new_in(data: T, alloc: A) -> Self {
let (ptr, alloc) = Box::into_unique(Box::new_in(
ArcInner {
strong: atomic::AtomicUsize::new(0),
// keep one weak reference so if all the weak pointers that are created are dropped
// the UniqueArc still stays valid.
weak: atomic::AtomicUsize::new(1),
data,
},
alloc,
));
Self { ptr: ptr.into(), phantom: PhantomData, alloc }
}
}

impl<T: ?Sized, A: Allocator> UniqueArc<T, A> {
/// Converts the `UniqueArc` into a regular [`Arc`].
///
/// This consumes the `UniqueArc` and returns a regular [`Arc`] that contains the `value` that
/// is passed to `into_arc`.
///
/// Any weak references created before this method is called can now be upgraded to strong
/// references.
#[unstable(feature = "unique_rc_arc", issue = "112566")]
pub fn into_arc(this: Self) -> Arc<T, A> {
let this = ManuallyDrop::new(this);

// Move the allocator out.
// SAFETY: `this.alloc` will not be accessed again, nor dropped because it is in
// a `ManuallyDrop`.
let alloc: A = unsafe { ptr::read(&this.alloc) };

// SAFETY: This pointer was allocated at creation time so we know it is valid.
unsafe {
// Convert our weak reference into a strong reference
(*this.ptr.as_ptr()).strong.store(1, Release);
Arc::from_inner_in(this.ptr, alloc)
}
}
}

impl<T: ?Sized, A: Allocator + Clone> UniqueArc<T, A> {
/// Creates a new weak reference to the `UniqueArc`.
///
/// Attempting to upgrade this weak reference will fail before the `UniqueArc` has been converted
/// to a [`Arc`] using [`UniqueArc::into_arc`].
#[unstable(feature = "unique_rc_arc", issue = "112566")]
pub fn downgrade(this: &Self) -> Weak<T, A> {
// Using a relaxed ordering is alright here, as knowledge of the
// original reference prevents other threads from erroneously deleting
// the object or converting the object to a normal `Arc<T, A>`.
//
// Note that we don't need to test if the weak counter is locked because there
// are no such operations like `Arc::get_mut` or `Arc::make_mut` that will lock
// the weak counter.
//
// SAFETY: This pointer was allocated at creation time so we know it is valid.
let old_size = unsafe { (*this.ptr.as_ptr()).weak.fetch_add(1, Relaxed) };

// See comments in Arc::clone() for why we do this (for mem::forget).
if old_size > MAX_REFCOUNT {
abort();
}

Weak { ptr: this.ptr, alloc: this.alloc.clone() }
}
}

#[unstable(feature = "unique_rc_arc", issue = "112566")]
impl<T: ?Sized, A: Allocator> Deref for UniqueArc<T, A> {
type Target = T;

fn deref(&self) -> &T {
// SAFETY: This pointer was allocated at creation time so we know it is valid.
unsafe { &self.ptr.as_ref().data }
}
}

// #[unstable(feature = "unique_rc_arc", issue = "112566")]
#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")]
unsafe impl<T: ?Sized> PinCoerceUnsized for UniqueArc<T> {}

#[unstable(feature = "unique_rc_arc", issue = "112566")]
impl<T: ?Sized, A: Allocator> DerefMut for UniqueArc<T, A> {
fn deref_mut(&mut self) -> &mut T {
// SAFETY: This pointer was allocated at creation time so we know it is valid. We know we
// have unique ownership and therefore it's safe to make a mutable reference because
// `UniqueArc` owns the only strong reference to itself.
unsafe { &mut (*self.ptr.as_ptr()).data }
}
}

#[unstable(feature = "unique_rc_arc", issue = "112566")]
unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueArc<T, A> {
fn drop(&mut self) {
// See `Arc::drop_slow` which drops an `Arc` with a strong count of 0.
// SAFETY: This pointer was allocated at creation time so we know it is valid.
let _weak = Weak { ptr: self.ptr, alloc: &self.alloc };

unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).data) };
}
}
5 changes: 4 additions & 1 deletion library/alloc/tests/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ fn make_mut_unsized() {

#[allow(unused)]
mod pin_coerce_unsized {
use alloc::sync::Arc;
use alloc::sync::{Arc, UniqueArc};
use core::pin::Pin;

pub trait MyTrait {}
Expand All @@ -276,4 +276,7 @@ mod pin_coerce_unsized {
pub fn pin_arc(arg: Pin<Arc<String>>) -> Pin<Arc<dyn MyTrait>> {
arg
}
pub fn pin_unique_arc(arg: Pin<UniqueArc<String>>) -> Pin<UniqueArc<dyn MyTrait>> {
arg
}
}
2 changes: 2 additions & 0 deletions library/std/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ pub use core::sync::Exclusive;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::sync::atomic;

#[unstable(feature = "unique_rc_arc", issue = "112566")]
pub use alloc_crate::sync::UniqueArc;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::sync::{Arc, Weak};

Expand Down

0 comments on commit 5142171

Please sign in to comment.