about summary refs log tree commit diff
path: root/src/libstd/rc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/rc.rs')
-rw-r--r--src/libstd/rc.rs29
1 files changed, 27 insertions, 2 deletions
diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs
index 7d0ddb2e4fb..a1565bc85de 100644
--- a/src/libstd/rc.rs
+++ b/src/libstd/rc.rs
@@ -50,7 +50,12 @@ impl<T> Rc<T> {
     pub fn new(value: T) -> Rc<T> {
         unsafe {
             Rc {
-                ptr: transmute(~RcBox { value: value, strong: 1, weak: 0 }),
+                // there is an implicit weak pointer owned by all the
+                // strong pointers, which ensures that the weak
+                // destructor never frees the allocation while the
+                // strong destructor is running, even if the weak
+                // pointer is stored inside the strong one.
+                ptr: transmute(~RcBox { value: value, strong: 1, weak: 1 }),
                 marker: marker::NoSend,
             }
         }
@@ -81,6 +86,11 @@ impl<T> Drop for Rc<T> {
                 (*self.ptr).strong -= 1;
                 if (*self.ptr).strong == 0 {
                     read_ptr(self.borrow()); // destroy the contained object
+
+                    // remove the implicit "strong weak" pointer now
+                    // that we've destroyed the contents.
+                    (*self.ptr).weak -= 1;
+
                     if (*self.ptr).weak == 0 {
                         exchange_free(self.ptr as *u8)
                     }
@@ -156,7 +166,9 @@ impl<T> Drop for Weak<T> {
         unsafe {
             if self.ptr != 0 as *mut RcBox<T> {
                 (*self.ptr).weak -= 1;
-                if (*self.ptr).weak == 0 && (*self.ptr).strong == 0 {
+                // the weak count starts at 1, and will only go to
+                // zero if all the strong pointers have disappeared.
+                if (*self.ptr).weak == 0 {
                     exchange_free(self.ptr as *u8)
                 }
             }
@@ -242,4 +254,17 @@ mod tests {
         let a = Rc::new(RefCell::new(Gc::new(1)));
         assert!(a.borrow().try_borrow_mut().is_some());
     }
+
+    #[test]
+    fn weak_self_cyclic() {
+        struct Cycle {
+            x: RefCell<Option<Weak<Cycle>>>
+        }
+
+        let a = Rc::new(Cycle { x: RefCell::new(None) });
+        let b = a.clone().downgrade();
+        *a.borrow().x.borrow_mut().get() = Some(b);
+
+        // hopefully we don't double-free (or leak)...
+    }
 }