From fee500a75189053b21e710a836778bf1062a4c51 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 12 Aug 2015 15:32:46 -0700 Subject: [PATCH] add Arc::try_unwrap --- src/liballoc/arc.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 052d1ccf63b8b..5602e8c55ceb8 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -191,6 +191,45 @@ impl Arc { }; Arc { _ptr: unsafe { NonZero::new(Box::into_raw(x)) } } } + + /// Unwraps the contained value if the `Arc` has only one strong reference. + /// This will succeed even if there are outstanding weak references. + /// + /// Otherwise, an `Err` is returned with the same `Arc`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// assert_eq!(Arc::try_unwrap(x), Ok(3)); + /// + /// let x = Arc::new(4); + /// let _y = x.clone(); + /// assert_eq!(Arc::try_unwrap(x), Err(Arc::new(4))); + /// ``` + #[inline] + #[unstable(feature = "arc_unwrap", reason = "was missing, so is technically new")] + pub fn try_unwrap(this: Arc) -> Result> { + // See `drop` for impl details + if self.inner().strong.compare_and_swap(1, 0, Release) != 1 { return Err(self) } + + // ~~ magic ~~ + atomic::fence(Acquire); + + unsafe { + let ptr = *self._ptr; + + let elem = ptr::read(&(*ptr).data); + + if self.inner().weak.fetch_sub(1, Release) == 1 { + atomic::fence(Acquire); + deallocate(ptr as *mut u8, size_of_val(&*ptr), align_of_val(&*ptr)) + } + Ok(elem) + } + } } impl Arc { @@ -885,6 +924,18 @@ mod tests { assert!(Arc::get_mut(&mut x).is_none()); } + #[test] + fn try_unwrap() { + let x = Arc::new(3); + assert_eq!(Arc::try_unwrap(x), Ok(3)); + let x = Arc::new(4); + let _y = x.clone(); + assert_eq!(Arc::try_unwrap(x), Err(Arc::new(4))); + let x = Arc::new(5); + let _w = x.downgrade(); + assert_eq!(Arc::try_unwrap(x), Ok(5)); + } + #[test] fn test_cowarc_clone_make_mut() { let mut cow0 = Arc::new(75);