]> git.lizzy.rs Git - rust.git/commitdiff
Make Arc::make_unique check weak refs; add make_unique to Rc
authorAaron Turon <aturon@mozilla.com>
Thu, 29 May 2014 18:49:01 +0000 (11:49 -0700)
committerAaron Turon <aturon@mozilla.com>
Thu, 29 May 2014 20:26:23 +0000 (13:26 -0700)
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
src/liballoc/rc.rs

index 546e4e5269979c27e773823d76beb39fd064a0ff..a8eb4b3407eb73fb55baf72a421d72ac9fc87076 100644 (file)
@@ -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
@@ -356,6 +360,20 @@ fn test_cowarc_clone_unique2() {
         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);
index 8ded3c431d4e2774b75297196b716be3e1c6baaf..96d90e6ed6395e44a9b6032a01d8b32f6ad606a8 100644 (file)
@@ -86,6 +86,31 @@ pub fn downgrade(&self) -> Weak<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 @@ fn inner<'a>(&'a self) -> &'a RcBox<T> { unsafe { &(*self._ptr) } }
 }
 
 #[cfg(test)]
+#[allow(experimental)]
 mod tests {
     use super::{Rc, Weak};
     use std::cell::RefCell;
@@ -304,4 +330,66 @@ struct Cycle {
 
         // 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());
+    }
+
 }