Skip to content

Commit 9a9a70b

Browse files
committed
auto merge of #12047 : huonw/rust/cyclic-rc, r=thestinger
A weak pointer inside itself will have its destructor run when the last strong pointer to that data disappears, so we need to make sure that the Weak and Rc destructors don't duplicate work (i.e. freeing). By making the Rcs effectively take a weak pointer, we ensure that no Weak destructor will free the pointer while still ensuring that Weak pointers can't be upgraded to strong ones as the destructors run. This approach of starting weak at 1 is what libstdc++ does. Fixes #12046.
2 parents d8c4e78 + da45340 commit 9a9a70b

File tree

1 file changed

+27
-2
lines changed

1 file changed

+27
-2
lines changed

src/libstd/rc.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ impl<T> Rc<T> {
5050
pub fn new(value: T) -> Rc<T> {
5151
unsafe {
5252
Rc {
53-
ptr: transmute(~RcBox { value: value, strong: 1, weak: 0 }),
53+
// there is an implicit weak pointer owned by all the
54+
// strong pointers, which ensures that the weak
55+
// destructor never frees the allocation while the
56+
// strong destructor is running, even if the weak
57+
// pointer is stored inside the strong one.
58+
ptr: transmute(~RcBox { value: value, strong: 1, weak: 1 }),
5459
marker: marker::NoSend,
5560
}
5661
}
@@ -81,6 +86,11 @@ impl<T> Drop for Rc<T> {
8186
(*self.ptr).strong -= 1;
8287
if (*self.ptr).strong == 0 {
8388
read_ptr(self.borrow()); // destroy the contained object
89+
90+
// remove the implicit "strong weak" pointer now
91+
// that we've destroyed the contents.
92+
(*self.ptr).weak -= 1;
93+
8494
if (*self.ptr).weak == 0 {
8595
exchange_free(self.ptr as *u8)
8696
}
@@ -156,7 +166,9 @@ impl<T> Drop for Weak<T> {
156166
unsafe {
157167
if self.ptr != 0 as *mut RcBox<T> {
158168
(*self.ptr).weak -= 1;
159-
if (*self.ptr).weak == 0 && (*self.ptr).strong == 0 {
169+
// the weak count starts at 1, and will only go to
170+
// zero if all the strong pointers have disappeared.
171+
if (*self.ptr).weak == 0 {
160172
exchange_free(self.ptr as *u8)
161173
}
162174
}
@@ -242,4 +254,17 @@ mod tests {
242254
let a = Rc::new(RefCell::new(Gc::new(1)));
243255
assert!(a.borrow().try_borrow_mut().is_some());
244256
}
257+
258+
#[test]
259+
fn weak_self_cyclic() {
260+
struct Cycle {
261+
x: RefCell<Option<Weak<Cycle>>>
262+
}
263+
264+
let a = Rc::new(Cycle { x: RefCell::new(None) });
265+
let b = a.clone().downgrade();
266+
*a.borrow().x.borrow_mut().get() = Some(b);
267+
268+
// hopefully we don't double-free (or leak)...
269+
}
245270
}

0 commit comments

Comments
 (0)