diff options
| author | Frank King <frankking1729@gmail.com> | 2024-11-28 21:57:39 +0800 |
|---|---|---|
| committer | Frank King <frankking1729@gmail.com> | 2025-03-22 15:14:49 +0800 |
| commit | 5016467a23c93384ffcb58b9c5ff2f8afc349d54 (patch) | |
| tree | 36157d672f353b0e3c78e658912870978fc6239e | |
| parent | 2a1c8beabf81bccf48970c5dbe34dfaaed20f86c (diff) | |
| download | rust-5016467a23c93384ffcb58b9c5ff2f8afc349d54.tar.gz rust-5016467a23c93384ffcb58b9c5ff2f8afc349d54.zip | |
Implement `UniqueArc`
| -rw-r--r-- | library/alloc/src/sync.rs | 182 | ||||
| -rw-r--r-- | library/alloctests/tests/arc.rs | 5 | ||||
| -rw-r--r-- | library/std/src/sync/mod.rs | 2 |
3 files changed, 187 insertions, 2 deletions
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index c62f8e5b70f..3903303bcea 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -20,7 +20,7 @@ use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; use core::num::NonZeroUsize; -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}; @@ -4066,3 +4066,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) }; + } +} diff --git a/library/alloctests/tests/arc.rs b/library/alloctests/tests/arc.rs index 0baa50f439b..45a145c6271 100644 --- a/library/alloctests/tests/arc.rs +++ b/library/alloctests/tests/arc.rs @@ -265,7 +265,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 {} @@ -275,4 +275,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 + } } diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 5b50a3c6ccf..e67b4f6f22f 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -176,6 +176,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}; |
