diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 73d2fe74826ed..d4ea362c81a75 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -500,6 +500,62 @@ impl Arc { Ok(elem) } } + + /// Returns the inner value, if the `Arc` has exactly one strong reference. + /// + /// Otherwise, [`None`] is returned and the `Arc` is dropped. + /// + /// This will succeed even if there are outstanding weak references. + /// + /// If `unwrap_or_drop` is called on every clone of this `Arc`, + /// it is guaranteed that exactly one of the calls returns the inner value. + /// The similar expression `Arc::try_unwrap(this).ok()` does not + /// offer this guarantee. + /// + /// # Examples + /// + /// ``` + /// #![feature(unwrap_or_drop)] + /// + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// let y = Arc::clone(&x); + /// + /// let x_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(x)); + /// let y_unwrap_thread = std::thread::spawn(|| Arc::unwrap_or_drop(y)); + /// + /// let x_unwrapped_value = x_unwrap_thread.join().unwrap(); + /// let y_unwrapped_value = y_unwrap_thread.join().unwrap(); + /// + /// assert!(matches!( + /// (x_unwrapped_value, y_unwrapped_value), + /// (None, Some(3)) | (Some(3), None) + /// )); + /// ``` + #[inline] + #[unstable(feature = "unwrap_or_drop", issue = "none")] // FIXME: add issue + // FIXME: should this copy all/some of the comments from drop and drop_slow? + pub fn unwrap_or_drop(this: Self) -> Option { + // following the implementation of `drop` (and `drop_slow`) + let mut this = core::mem::ManuallyDrop::new(this); + + if this.inner().strong.fetch_sub(1, Release) != 1 { + return None; + } + + acquire!(this.inner().strong); + + // FIXME: should the part below this be moved into a seperate #[inline(never)] + // function, like it's done with drop_slow in drop? + + // using `ptr::read` where `drop_slow` was using `ptr::drop_in_place` + let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) }; + + drop(Weak { ptr: this.ptr }); + + Some(inner) + } } impl Arc<[T]> { diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs index d25171716061d..10b05b2fd0f31 100644 --- a/library/alloc/src/sync/tests.rs +++ b/library/alloc/src/sync/tests.rs @@ -101,6 +101,45 @@ fn try_unwrap() { assert_eq!(Arc::try_unwrap(x), Ok(5)); } +#[test] +fn unwrap_or_drop() { + // FIXME: Is doing this kind of loop reasonable? I tested `Arc::try_unwrap(x).ok()` + // and it makes this kind of assertion fail in roughly every second run somewhere + // between 1000 and 5000 iterations; I feel like doing a single iteration is too + // unlikely to catch anything interesting but doing too many is way too slow + // for a test that wouldn't ever fail for any reasonable implementation + + for _ in 0..100 + // ^ increase chances of hitting uncommon race conditions + { + use std::sync::Arc; + let x = Arc::new(3); + let y = Arc::clone(&x); + let r_thread = std::thread::spawn(|| Arc::try_unwrap(x).ok()); + let s_thread = std::thread::spawn(|| Arc::try_unwrap(y).ok()); + let r = r_thread.join().expect("r_thread panicked"); + let s = s_thread.join().expect("s_thread panicked"); + assert!( + matches!((r, s), (None, Some(3)) | (Some(3), None)), + "assertion failed: unexpected result `{:?}`\ + \n expected `(None, Some(3))` or `(Some(3), None)`", + (r, s), + ); + } + + let x = Arc::new(3); + assert_eq!(Arc::unwrap_or_drop(x), Some(3)); + + let x = Arc::new(4); + let y = Arc::clone(&x); + assert_eq!(Arc::unwrap_or_drop(x), None); + assert_eq!(Arc::unwrap_or_drop(y), Some(4)); + + let x = Arc::new(5); + let _w = Arc::downgrade(&x); + assert_eq!(Arc::unwrap_or_drop(x), Some(5)); +} + #[test] fn into_from_raw() { let x = Arc::new(box "hello");