diff options
| author | Aaron Turon <aturon@mozilla.com> | 2014-05-29 11:49:01 -0700 |
|---|---|---|
| committer | Aaron Turon <aturon@mozilla.com> | 2014-05-29 13:26:23 -0700 |
| commit | 7889c951240624b22cc4c9b87f2852322b0b716c (patch) | |
| tree | 050eb9bdcdcfc89650cfb9c13d2c40a3ccd33f20 /src/liballoc | |
| parent | 50b8528970734adcc654740c51c50094f73dcbee (diff) | |
| download | rust-7889c951240624b22cc4c9b87f2852322b0b716c.tar.gz rust-7889c951240624b22cc4c9b87f2852322b0b716c.zip | |
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.
Diffstat (limited to 'src/liballoc')
| -rw-r--r-- | src/liballoc/arc.rs | 20 | ||||
| -rw-r--r-- | src/liballoc/rc.rs | 88 |
2 files changed, 107 insertions, 1 deletions
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<T: Send + Share + Clone> Arc<T> { #[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 @@ -357,6 +361,20 @@ mod tests { } #[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); let y = x.downgrade(); 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<T> Rc<T> { } } +impl<T: Clone> Rc<T> { + /// 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<T> Deref<T> for Rc<T> { /// Borrow the value contained in the reference-counted box #[inline(always)] @@ -234,6 +259,7 @@ impl<T> RcBoxPtr<T> for Weak<T> { } #[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()); + } + } |
