Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Arc::is_unique #138939

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 66 additions & 8 deletions library/alloc/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2446,7 +2446,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> {
#[inline]
#[stable(feature = "arc_unique", since = "1.4.0")]
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
if this.is_unique() {
if Self::is_unique(this) {
// This unsafety is ok because we're guaranteed that the pointer
// returned is the *only* pointer that will ever be returned to T. Our
// reference count is guaranteed to be 1 at this point, and we required
Expand Down Expand Up @@ -2526,28 +2526,86 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> {
unsafe { &mut (*this.ptr.as_ptr()).data }
}

/// Determine whether this is the unique reference (including weak refs) to
/// the underlying data.
/// Determine whether this is the unique reference to the underlying data.
/// Returns `true` if there are no other `Arc` or [`Weak`] pointers to the same allocation;
/// returns `false` otherwise.
///
/// Note that this requires locking the weak ref count.
fn is_unique(&mut self) -> bool {
/// If this function returns `true`,
/// then is guaranteed to be safe to call [`get_mut_unchecked`] on this `Arc`,
/// so long as no clones occur in between.
///
/// # Examples
///
/// ```
/// #![feature(arc_is_unique)]
///
/// use std::sync::Arc;
///
/// let x = Arc::new(3);
/// assert!(Arc::is_unique(&x));
///
/// let y = Arc::clone(&x);
/// assert!(!Arc::is_unique(&x));
/// drop(y);
///
/// // Weak references also count, because they could be upgraded at any time.
/// let z = Arc::downgrade(&x);
/// assert!(!Arc::is_unique(&x));
/// ```
///
/// # Pointer invalidation
///
/// This function
/// will always return the same value as `Arc::get_mut(arc).is_some()`.
/// However, unlike that operation
/// it does not produce any mutable references to the underlying data,
/// meaning no pointers to the data inside the `Arc` are invalidated by the call.
/// Thus, the following code is valid,
/// even though it would be UB if it used `Arc::get_mut`:
///
/// ```
/// #![feature(arc_is_unique)]
///
/// use std::sync::Arc;
///
/// let arc = Arc::new(5);
/// let pointer: *const i32 = &*arc;
/// assert!(Arc::is_unique(&arc));
/// assert_eq!(unsafe { *pointer }, 5);
/// ```
///
/// # Atomic orderings
///
/// Concurrent drops to other `Arc` pointers to the same allocation
/// will synchronize with this call -
/// that is, this call performs an `Acquire` operation
/// on the underlying strong and weak ref counts.
/// This ensures that calling `get_mut_unchecked` is safe.
///
/// Note that this operation requires locking the weak ref count,
/// so concurrent calls to `downgrade` may spin-loop for a short period of time.
///
/// [`get_mut_unchecked`]: Self::get_mut_unchecked
#[inline]
#[unstable(feature = "arc_is_unique", issue = "138938")]
pub fn is_unique(this: &Self) -> bool {
// lock the weak pointer count if we appear to be the sole weak pointer
// holder.
//
// The acquire label here ensures a happens-before relationship with any
// writes to `strong` (in particular in `Weak::upgrade`) prior to decrements
// of the `weak` count (via `Weak::drop`, which uses release). If the upgraded
// weak ref was never dropped, the CAS here will fail so we do not care to synchronize.
if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() {
if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() {
// This needs to be an `Acquire` to synchronize with the decrement of the `strong`
// counter in `drop` -- the only access that happens when any but the last reference
// is being dropped.
let unique = self.inner().strong.load(Acquire) == 1;
let unique = this.inner().strong.load(Acquire) == 1;

// The release write here synchronizes with a read in `downgrade`,
// effectively preventing the above read of `strong` from happening
// after the write.
self.inner().weak.store(1, Release); // release the lock
this.inner().weak.store(1, Release); // release the lock
unique
} else {
false
Expand Down
Loading