From 2fe3b3b486b3b5e1ec1e6ad259d8840b88b7c77a Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 11 Dec 2018 00:40:39 +0100 Subject: [PATCH 1/4] Implement Weak::{strong_count, weak_count} --- src/liballoc/rc.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++ src/liballoc/sync.rs | 19 ++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index c0dc010fe59a5..7ee05eaff91a0 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1284,6 +1284,40 @@ impl Weak { } } + /// Gets the number of strong (`Rc`) pointers pointing to this value. + /// + /// If `self` was created using [`Weak::new`], this will return 0. + /// + /// [`Weak::new`]: #method.new + #[unstable(feature = "weak_counts", issue = "0")] + pub fn strong_count(&self) -> usize { + if let Some(inner) = self.inner() { + inner.strong() + } else { + 0 + } + } + + /// Gets the number of `Weak` pointers pointing to this value. + /// + /// If `self` was created using [`Weak::new`], this will return 0. If not, + /// the returned value is at least 1, since `self` still points to the + /// value. + /// + /// [`Weak::new`]: #method.new + #[unstable(feature = "weak_counts", issue = "0")] + pub fn weak_count(&self) -> usize { + if let Some(inner) = self.inner() { + if inner.strong() > 0 { + inner.weak() - 1 // subtract the implicit weak ptr + } else { + inner.weak() + } + } else { + 0 + } + } + /// Return `None` when the pointer is dangling and there is no allocated `RcBox`, /// i.e., this `Weak` was created by `Weak::new` #[inline] @@ -1622,6 +1656,33 @@ mod tests { drop(c); } + #[test] + fn weak_counts() { + assert_eq!(Weak::weak_count(&Weak::::new()), 0); + assert_eq!(Weak::strong_count(&Weak::::new()), 0); + + let a = Rc::new(0); + let w = Rc::downgrade(&a); + assert_eq!(Weak::strong_count(&w), 1); + assert_eq!(Weak::weak_count(&w), 1); + let w2 = w.clone(); + assert_eq!(Weak::strong_count(&w), 1); + assert_eq!(Weak::weak_count(&w), 2); + assert_eq!(Weak::strong_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), 2); + drop(w); + assert_eq!(Weak::strong_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), 1); + let a2 = a.clone(); + assert_eq!(Weak::strong_count(&w2), 2); + assert_eq!(Weak::weak_count(&w2), 1); + drop(a2); + drop(a); + assert_eq!(Weak::strong_count(&w2), 0); + assert_eq!(Weak::weak_count(&w2), 1); + drop(w2); + } + #[test] fn try_unwrap() { let x = Rc::new(3); diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 390a079165054..fd3519dee18cf 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -1117,6 +1117,25 @@ impl Weak { } } + /// Gets the number of strong (`Arc`) pointers pointing to this value. + /// + /// If `self` was created using [`Weak::new`], this will return 0. + /// + /// [`Weak::new`]: #method.new + #[unstable(feature = "weak_counts", issue = "0")] + pub fn strong_count(&self) -> usize { + if let Some(inner) = self.inner() { + inner.strong.load(SeqCst) + } else { + 0 + } + } + + // Due to the implicit weak pointer added when any strong pointers are + // around, we cannot implement `weak_count` correctly since it necessarily + // requires accessing the strong count and weak count in an unsynchronized + // fashion. + /// Return `None` when the pointer is dangling and there is no allocated `ArcInner`, /// i.e., this `Weak` was created by `Weak::new` #[inline] From 7e0edb39baa02c58c373f05014ffd8e3058c93ad Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 7 Jan 2019 23:35:52 +0100 Subject: [PATCH 2/4] Implement a slightly racy `sync::Weak::weak_count` --- src/liballoc/sync.rs | 75 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index fd3519dee18cf..b065bd60c60a7 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -11,7 +11,7 @@ use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; use core::borrow; use core::fmt; -use core::cmp::Ordering; +use core::cmp::{self, Ordering}; use core::intrinsics::abort; use core::mem::{self, align_of_val, size_of_val}; use core::ops::{Deref, Receiver}; @@ -1131,10 +1131,48 @@ impl Weak { } } - // Due to the implicit weak pointer added when any strong pointers are - // around, we cannot implement `weak_count` correctly since it necessarily - // requires accessing the strong count and weak count in an unsynchronized - // fashion. + /// Gets an approximation of the number of `Weak` pointers pointing to this + /// value. + /// + /// If `self` was created using [`Weak::new`], this will return 0. If not, + /// the returned value is at least 1, since `self` still points to the + /// value. + /// + /// # Accuracy + /// + /// Due to implementation details, the returned value can be off by 1 in + /// either direction when other threads are manipulating any `Arc`s or + /// `Weak`s pointing to the same value. + /// + /// [`Weak::new`]: #method.new + #[unstable(feature = "weak_counts", issue = "0")] + pub fn weak_count(&self) -> usize { + // Due to the implicit weak pointer added when any strong pointers are + // around, we cannot implement `weak_count` correctly since it + // necessarily requires accessing the strong count and weak count in an + // unsynchronized fashion. So this version is a bit racy. + if let Some(inner) = self.inner() { + let strong = inner.strong.load(SeqCst); + let weak = inner.weak.load(SeqCst); + if strong == 0 { + // If the last `Arc` has *just* been dropped, it might not yet + // have removed the implicit weak count, so the value we get + // here might be 1 too high. + weak + } else { + // As long as there's still at least 1 `Arc` around, subtract + // the implicit weak pointer. + // Note that the last `Arc` might get dropped between the 2 + // loads we do above, removing the implicit weak pointer. This + // means that the value might be 1 too low here. In order to not + // return 0 here (which would happen if we're the only weak + // pointer), we guard against that specifically. + cmp::max(1, weak - 1) + } + } else { + 0 + } + } /// Return `None` when the pointer is dangling and there is no allocated `ArcInner`, /// i.e., this `Weak` was created by `Weak::new` @@ -1655,6 +1693,33 @@ mod tests { assert!(Arc::get_mut(&mut x).is_none()); } + #[test] + fn weak_counts() { + assert_eq!(Weak::weak_count(&Weak::::new()), 0); + assert_eq!(Weak::strong_count(&Weak::::new()), 0); + + let a = Arc::new(0); + let w = Arc::downgrade(&a); + assert_eq!(Weak::strong_count(&w), 1); + assert_eq!(Weak::weak_count(&w), 1); + let w2 = w.clone(); + assert_eq!(Weak::strong_count(&w), 1); + assert_eq!(Weak::weak_count(&w), 2); + assert_eq!(Weak::strong_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), 2); + drop(w); + assert_eq!(Weak::strong_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), 1); + let a2 = a.clone(); + assert_eq!(Weak::strong_count(&w2), 2); + assert_eq!(Weak::weak_count(&w2), 1); + drop(a2); + drop(a); + assert_eq!(Weak::strong_count(&w2), 0); + assert_eq!(Weak::weak_count(&w2), 1); + drop(w2); + } + #[test] fn try_unwrap() { let x = Arc::new(3); From b664341d917cafade5a2470579a4c122fdcf941b Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 29 Jan 2019 21:58:17 +0100 Subject: [PATCH 3/4] Make weak_count return an Option --- src/liballoc/rc.rs | 26 ++++++++++++-------------- src/liballoc/sync.rs | 22 ++++++++++------------ 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 7ee05eaff91a0..72b43b5d6adff 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1300,22 +1300,20 @@ impl Weak { /// Gets the number of `Weak` pointers pointing to this value. /// - /// If `self` was created using [`Weak::new`], this will return 0. If not, - /// the returned value is at least 1, since `self` still points to the + /// If `self` was created using [`Weak::new`], this will return `None`. If + /// not, the returned value is at least 1, since `self` still points to the /// value. /// /// [`Weak::new`]: #method.new #[unstable(feature = "weak_counts", issue = "0")] - pub fn weak_count(&self) -> usize { - if let Some(inner) = self.inner() { + pub fn weak_count(&self) -> Option { + self.inner().map(|inner| { if inner.strong() > 0 { inner.weak() - 1 // subtract the implicit weak ptr } else { inner.weak() } - } else { - 0 - } + }) } /// Return `None` when the pointer is dangling and there is no allocated `RcBox`, @@ -1658,28 +1656,28 @@ mod tests { #[test] fn weak_counts() { - assert_eq!(Weak::weak_count(&Weak::::new()), 0); + assert_eq!(Weak::weak_count(&Weak::::new()), None); assert_eq!(Weak::strong_count(&Weak::::new()), 0); let a = Rc::new(0); let w = Rc::downgrade(&a); assert_eq!(Weak::strong_count(&w), 1); - assert_eq!(Weak::weak_count(&w), 1); + assert_eq!(Weak::weak_count(&w), Some(1)); let w2 = w.clone(); assert_eq!(Weak::strong_count(&w), 1); - assert_eq!(Weak::weak_count(&w), 2); + assert_eq!(Weak::weak_count(&w), Some(2)); assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), 2); + assert_eq!(Weak::weak_count(&w2), Some(2)); drop(w); assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), Some(1)); let a2 = a.clone(); assert_eq!(Weak::strong_count(&w2), 2); - assert_eq!(Weak::weak_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), Some(1)); drop(a2); drop(a); assert_eq!(Weak::strong_count(&w2), 0); - assert_eq!(Weak::weak_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), Some(1)); drop(w2); } diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index b065bd60c60a7..777851e192501 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -1146,12 +1146,12 @@ impl Weak { /// /// [`Weak::new`]: #method.new #[unstable(feature = "weak_counts", issue = "0")] - pub fn weak_count(&self) -> usize { + pub fn weak_count(&self) -> Option { // Due to the implicit weak pointer added when any strong pointers are // around, we cannot implement `weak_count` correctly since it // necessarily requires accessing the strong count and weak count in an // unsynchronized fashion. So this version is a bit racy. - if let Some(inner) = self.inner() { + self.inner().map(|inner| { let strong = inner.strong.load(SeqCst); let weak = inner.weak.load(SeqCst); if strong == 0 { @@ -1169,9 +1169,7 @@ impl Weak { // pointer), we guard against that specifically. cmp::max(1, weak - 1) } - } else { - 0 - } + }) } /// Return `None` when the pointer is dangling and there is no allocated `ArcInner`, @@ -1695,28 +1693,28 @@ mod tests { #[test] fn weak_counts() { - assert_eq!(Weak::weak_count(&Weak::::new()), 0); + assert_eq!(Weak::weak_count(&Weak::::new()), None); assert_eq!(Weak::strong_count(&Weak::::new()), 0); let a = Arc::new(0); let w = Arc::downgrade(&a); assert_eq!(Weak::strong_count(&w), 1); - assert_eq!(Weak::weak_count(&w), 1); + assert_eq!(Weak::weak_count(&w), Some(1)); let w2 = w.clone(); assert_eq!(Weak::strong_count(&w), 1); - assert_eq!(Weak::weak_count(&w), 2); + assert_eq!(Weak::weak_count(&w), Some(2)); assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), 2); + assert_eq!(Weak::weak_count(&w2), Some(2)); drop(w); assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), Some(1)); let a2 = a.clone(); assert_eq!(Weak::strong_count(&w2), 2); - assert_eq!(Weak::weak_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), Some(1)); drop(a2); drop(a); assert_eq!(Weak::strong_count(&w2), 0); - assert_eq!(Weak::weak_count(&w2), 1); + assert_eq!(Weak::weak_count(&w2), Some(1)); drop(w2); } From 0d314f08af086afd7a67131a621f861b2633d6d3 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 29 Jan 2019 22:34:35 +0100 Subject: [PATCH 4/4] Add tracking issue to unstable attribute --- src/liballoc/rc.rs | 4 ++-- src/liballoc/sync.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 72b43b5d6adff..235a89c9e9db4 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1289,7 +1289,7 @@ impl Weak { /// If `self` was created using [`Weak::new`], this will return 0. /// /// [`Weak::new`]: #method.new - #[unstable(feature = "weak_counts", issue = "0")] + #[unstable(feature = "weak_counts", issue = "57977")] pub fn strong_count(&self) -> usize { if let Some(inner) = self.inner() { inner.strong() @@ -1305,7 +1305,7 @@ impl Weak { /// value. /// /// [`Weak::new`]: #method.new - #[unstable(feature = "weak_counts", issue = "0")] + #[unstable(feature = "weak_counts", issue = "57977")] pub fn weak_count(&self) -> Option { self.inner().map(|inner| { if inner.strong() > 0 { diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 777851e192501..d08aaa38a4c0a 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -1122,7 +1122,7 @@ impl Weak { /// If `self` was created using [`Weak::new`], this will return 0. /// /// [`Weak::new`]: #method.new - #[unstable(feature = "weak_counts", issue = "0")] + #[unstable(feature = "weak_counts", issue = "57977")] pub fn strong_count(&self) -> usize { if let Some(inner) = self.inner() { inner.strong.load(SeqCst) @@ -1145,7 +1145,7 @@ impl Weak { /// `Weak`s pointing to the same value. /// /// [`Weak::new`]: #method.new - #[unstable(feature = "weak_counts", issue = "0")] + #[unstable(feature = "weak_counts", issue = "57977")] pub fn weak_count(&self) -> Option { // Due to the implicit weak pointer added when any strong pointers are // around, we cannot implement `weak_count` correctly since it