From 7889c951240624b22cc4c9b87f2852322b0b716c Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Thu, 29 May 2014 11:49:01 -0700 Subject: [PATCH] Make Arc::make_unique check weak refs; add make_unique to Rc This patch makes `Arc::make_unique` examine the number of weak references as well as strong references, which is required for safety. It also adds a `make_unique` method to the `Rc` type for consistency. Closes #14521. --- src/liballoc/arc.rs | 20 ++++++++++- src/liballoc/rc.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 546e4e52699..a8eb4b3407e 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -152,7 +152,11 @@ impl Arc { #[inline] #[experimental] pub fn make_unique<'a>(&'a mut self) -> &'a mut T { - if self.inner().strong.load(atomics::SeqCst) != 1 { + // Note that we hold a strong reference, which also counts as + // a weak reference, so we only clone if there is an + // additional reference of either kind. + if self.inner().strong.load(atomics::SeqCst) != 1 || + self.inner().weak.load(atomics::SeqCst) != 1 { *self = Arc::new(self.deref().clone()) } // This unsafety is ok because we're guaranteed that the pointer @@ -356,6 +360,20 @@ mod tests { assert!(*cow1 == *cow2); } + #[test] + fn test_cowarc_clone_weak() { + let mut cow0 = Arc::new(75u); + let cow1_weak = cow0.downgrade(); + + assert!(75 == *cow0); + assert!(75 == *cow1_weak.upgrade().unwrap()); + + *cow0.make_unique() += 1; + + assert!(76 == *cow0); + assert!(cow1_weak.upgrade().is_none()); + } + #[test] fn test_live() { let x = Arc::new(5); diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 8ded3c431d4..96d90e6ed63 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -86,6 +86,31 @@ impl Rc { } } +impl Rc { + /// Acquires a mutable pointer to the inner contents by guaranteeing that + /// the reference count is one (no sharing is possible). + /// + /// This is also referred to as a copy-on-write operation because the inner + /// data is cloned if the reference count is greater than one. + #[inline] + #[experimental] + pub fn make_unique<'a>(&'a mut self) -> &'a mut T { + // Note that we hold a strong reference, which also counts as + // a weak reference, so we only clone if there is an + // additional reference of either kind. + if self.strong() != 1 || self.weak() != 1 { + *self = Rc::new(self.deref().clone()) + } + // 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 + // the Rc itself to be `mut`, so we're returning the only possible + // reference to the inner data. + let inner = unsafe { &mut *self._ptr }; + &mut inner.value + } +} + impl Deref for Rc { /// Borrow the value contained in the reference-counted box #[inline(always)] @@ -234,6 +259,7 @@ impl RcBoxPtr for Weak { } #[cfg(test)] +#[allow(experimental)] mod tests { use super::{Rc, Weak}; use std::cell::RefCell; @@ -304,4 +330,66 @@ mod tests { // hopefully we don't double-free (or leak)... } + + #[test] + fn test_cowrc_clone_make_unique() { + let mut cow0 = Rc::new(75u); + let mut cow1 = cow0.clone(); + let mut cow2 = cow1.clone(); + + assert!(75 == *cow0.make_unique()); + assert!(75 == *cow1.make_unique()); + assert!(75 == *cow2.make_unique()); + + *cow0.make_unique() += 1; + *cow1.make_unique() += 2; + *cow2.make_unique() += 3; + + assert!(76 == *cow0); + assert!(77 == *cow1); + assert!(78 == *cow2); + + // none should point to the same backing memory + assert!(*cow0 != *cow1); + assert!(*cow0 != *cow2); + assert!(*cow1 != *cow2); + } + + #[test] + fn test_cowrc_clone_unique2() { + let mut cow0 = Rc::new(75u); + let cow1 = cow0.clone(); + let cow2 = cow1.clone(); + + assert!(75 == *cow0); + assert!(75 == *cow1); + assert!(75 == *cow2); + + *cow0.make_unique() += 1; + + assert!(76 == *cow0); + assert!(75 == *cow1); + assert!(75 == *cow2); + + // cow1 and cow2 should share the same contents + // cow0 should have a unique reference + assert!(*cow0 != *cow1); + assert!(*cow0 != *cow2); + assert!(*cow1 == *cow2); + } + + #[test] + fn test_cowrc_clone_weak() { + let mut cow0 = Rc::new(75u); + let cow1_weak = cow0.downgrade(); + + assert!(75 == *cow0); + assert!(75 == *cow1_weak.upgrade().unwrap()); + + *cow0.make_unique() += 1; + + assert!(76 == *cow0); + assert!(cow1_weak.upgrade().is_none()); + } + }