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 Rc { pub fn new(value: T) -> Rc { 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 Drop for Rc { (*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 Drop for Weak { unsafe { if self.ptr != 0 as *mut RcBox { (*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>> + } + + 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)... + } }