From 91ee3d1c31e5d1684e0d2ec5036dc69993b6f992 Mon Sep 17 00:00:00 2001 From: Bryan Donlan Date: Thu, 24 Oct 2019 20:09:35 +0000 Subject: [PATCH 1/3] Stabilize `std::{rc,sync}::Weak::{weak_count, strong_count}` Closes: #57977 --- 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 a11f9e8c14579..12f86bf880f13 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1803,7 +1803,7 @@ impl Weak { /// If `self` was created using [`Weak::new`], this will return 0. /// /// [`Weak::new`]: #method.new - #[unstable(feature = "weak_counts", issue = "57977")] + #[stable(feature = "weak_counts", since = "1.40.0")] pub fn strong_count(&self) -> usize { if let Some(inner) = self.inner() { inner.strong() @@ -1819,7 +1819,7 @@ impl Weak { /// allocation. /// /// [`Weak::new`]: #method.new - #[unstable(feature = "weak_counts", issue = "57977")] + #[stable(feature = "weak_counts", since = "1.40.0")] 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 4b10f089c2950..3f86dfb469eab 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -1496,7 +1496,7 @@ impl Weak { /// If `self` was created using [`Weak::new`], this will return 0. /// /// [`Weak::new`]: #method.new - #[unstable(feature = "weak_counts", issue = "57977")] + #[stable(feature = "weak_counts", since = "1.40.0")] pub fn strong_count(&self) -> usize { if let Some(inner) = self.inner() { inner.strong.load(SeqCst) @@ -1519,7 +1519,7 @@ impl Weak { /// `Weak`s pointing to the same allocation. /// /// [`Weak::new`]: #method.new - #[unstable(feature = "weak_counts", issue = "57977")] + #[stable(feature = "weak_counts", since = "1.40.0")] 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 From 0d0b283c2c5a4df891ca47b27f0851ef2549ac3b Mon Sep 17 00:00:00 2001 From: Bryan Donlan Date: Thu, 21 Nov 2019 19:48:39 +0000 Subject: [PATCH 2/3] Make Weak::weak_count() return zero when no strong refs remain --- src/liballoc/rc.rs | 12 ++++-------- src/liballoc/rc/tests.rs | 14 +++++++------- src/liballoc/sync.rs | 36 +++++++++++++----------------------- src/liballoc/sync/tests.rs | 14 +++++++------- 4 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 12f86bf880f13..0dab68b155d87 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1814,20 +1814,16 @@ impl Weak { /// Gets the number of `Weak` pointers pointing to this allocation. /// - /// 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 - /// allocation. - /// - /// [`Weak::new`]: #method.new + /// If no strong pointers remain, this will return zero. #[stable(feature = "weak_counts", since = "1.40.0")] - pub fn weak_count(&self) -> Option { + pub fn weak_count(&self) -> usize { self.inner().map(|inner| { if inner.strong() > 0 { inner.weak() - 1 // subtract the implicit weak ptr } else { - inner.weak() + 0 } - }) + }).unwrap_or(0) } /// Returns `None` when the pointer is dangling and there is no allocated `RcBox` diff --git a/src/liballoc/rc/tests.rs b/src/liballoc/rc/tests.rs index 6fd3f90935714..bf5c85a5c5960 100644 --- a/src/liballoc/rc/tests.rs +++ b/src/liballoc/rc/tests.rs @@ -114,28 +114,28 @@ fn test_weak_count() { #[test] fn weak_counts() { - assert_eq!(Weak::weak_count(&Weak::::new()), None); + 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), Some(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), Some(2)); + assert_eq!(Weak::weak_count(&w), 2); assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), Some(2)); + assert_eq!(Weak::weak_count(&w2), 2); drop(w); assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), Some(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), Some(1)); + assert_eq!(Weak::weak_count(&w2), 1); drop(a2); drop(a); assert_eq!(Weak::strong_count(&w2), 0); - assert_eq!(Weak::weak_count(&w2), Some(1)); + assert_eq!(Weak::weak_count(&w2), 0); drop(w2); } diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 3f86dfb469eab..1bfe3b802496b 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -12,7 +12,7 @@ use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; use core::borrow; use core::fmt; -use core::cmp::{self, Ordering}; +use core::cmp::Ordering; use core::iter; use core::intrinsics::abort; use core::mem::{self, align_of, align_of_val, size_of_val}; @@ -1508,9 +1508,8 @@ impl Weak { /// Gets an approximation of the number of `Weak` pointers pointing to this /// allocation. /// - /// 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 - /// allocation. + /// If `self` was created using [`Weak::new`], or if there are no remaining + /// strong pointers, this will return 0. /// /// # Accuracy /// @@ -1520,30 +1519,21 @@ impl Weak { /// /// [`Weak::new`]: #method.new #[stable(feature = "weak_counts", since = "1.40.0")] - 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. + pub fn weak_count(&self) -> usize { self.inner().map(|inner| { - let strong = inner.strong.load(SeqCst); let weak = inner.weak.load(SeqCst); + let strong = inner.strong.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 + 0 } 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) + // Since we observed that there was at least one strong pointer + // after reading the weak count, we know that the implicit weak + // reference (present whenever any strong references are alive) + // was still around when we observed the weak count, and can + // therefore safely subtract it. + weak - 1 } - }) + }).unwrap_or(0) } /// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`, diff --git a/src/liballoc/sync/tests.rs b/src/liballoc/sync/tests.rs index 9220f5e0333ef..be0200c9a4613 100644 --- a/src/liballoc/sync/tests.rs +++ b/src/liballoc/sync/tests.rs @@ -62,28 +62,28 @@ fn test_arc_get_mut() { #[test] fn weak_counts() { - assert_eq!(Weak::weak_count(&Weak::::new()), None); + 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), Some(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), Some(2)); + assert_eq!(Weak::weak_count(&w), 2); assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), Some(2)); + assert_eq!(Weak::weak_count(&w2), 2); drop(w); assert_eq!(Weak::strong_count(&w2), 1); - assert_eq!(Weak::weak_count(&w2), Some(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), Some(1)); + assert_eq!(Weak::weak_count(&w2), 1); drop(a2); drop(a); assert_eq!(Weak::strong_count(&w2), 0); - assert_eq!(Weak::weak_count(&w2), Some(1)); + assert_eq!(Weak::weak_count(&w2), 0); drop(w2); } From 9778e03665edbed80eb684ba893abd4e18a0a583 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 14 Dec 2019 19:26:25 -0800 Subject: [PATCH 3/3] Bump Weak::strong_count/weak_count stabilizations from 1.40 to 1.41 --- 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 0dab68b155d87..8cfb9fdf8ea93 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1803,7 +1803,7 @@ impl Weak { /// If `self` was created using [`Weak::new`], this will return 0. /// /// [`Weak::new`]: #method.new - #[stable(feature = "weak_counts", since = "1.40.0")] + #[stable(feature = "weak_counts", since = "1.41.0")] pub fn strong_count(&self) -> usize { if let Some(inner) = self.inner() { inner.strong() @@ -1815,7 +1815,7 @@ impl Weak { /// Gets the number of `Weak` pointers pointing to this allocation. /// /// If no strong pointers remain, this will return zero. - #[stable(feature = "weak_counts", since = "1.40.0")] + #[stable(feature = "weak_counts", since = "1.41.0")] pub fn weak_count(&self) -> usize { self.inner().map(|inner| { if inner.strong() > 0 { diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 1bfe3b802496b..d7653f78598ec 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -1496,7 +1496,7 @@ impl Weak { /// If `self` was created using [`Weak::new`], this will return 0. /// /// [`Weak::new`]: #method.new - #[stable(feature = "weak_counts", since = "1.40.0")] + #[stable(feature = "weak_counts", since = "1.41.0")] pub fn strong_count(&self) -> usize { if let Some(inner) = self.inner() { inner.strong.load(SeqCst) @@ -1518,7 +1518,7 @@ impl Weak { /// `Weak`s pointing to the same allocation. /// /// [`Weak::new`]: #method.new - #[stable(feature = "weak_counts", since = "1.40.0")] + #[stable(feature = "weak_counts", since = "1.41.0")] pub fn weak_count(&self) -> usize { self.inner().map(|inner| { let weak = inner.weak.load(SeqCst);