Skip to content

Commit c34ea91

Browse files
authored
Rollup merge of rust-lang#65778 - bdonlan:stable_weak_count, r=dtolnay
Stabilize `std::{rc,sync}::Weak::{weak_count, strong_count}` * Original PR: rust-lang#56696 * Tracking issue: rust-lang#57977 Closes: rust-lang#57977 Supporting comments: > Although these were added for testing, it is occasionally useful to have a way to probe optimistically for whether a weak pointer has become dangling, without actually taking the overhead of manipulating atomics. Are there any plans to stabilize this? _Originally posted by @bdonlan in rust-lang#57977 (comment) > Having this stabilized would help. Currently, the only way to check if a weak pointer has become dangling is to call `upgrade`, which is by far expensive. _Originally posted by @glebpom in rust-lang#57977 (comment) Not sure if stabilizing these warrants a full RFC, so throwing this out here as a start for now. Note: per CONTRIBUTING.md, I ran the tidy checks, but they seem to be failing on unchanged files (primarily in `src/stdsimd`).
2 parents a605441 + 9778e03 commit c34ea91

File tree

4 files changed

+35
-49
lines changed

4 files changed

+35
-49
lines changed

src/liballoc/rc.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,7 @@ impl<T: ?Sized> Weak<T> {
18361836
/// If `self` was created using [`Weak::new`], this will return 0.
18371837
///
18381838
/// [`Weak::new`]: #method.new
1839-
#[unstable(feature = "weak_counts", issue = "57977")]
1839+
#[stable(feature = "weak_counts", since = "1.41.0")]
18401840
pub fn strong_count(&self) -> usize {
18411841
if let Some(inner) = self.inner() {
18421842
inner.strong()
@@ -1847,20 +1847,16 @@ impl<T: ?Sized> Weak<T> {
18471847

18481848
/// Gets the number of `Weak` pointers pointing to this allocation.
18491849
///
1850-
/// If `self` was created using [`Weak::new`], this will return `None`. If
1851-
/// not, the returned value is at least 1, since `self` still points to the
1852-
/// allocation.
1853-
///
1854-
/// [`Weak::new`]: #method.new
1855-
#[unstable(feature = "weak_counts", issue = "57977")]
1856-
pub fn weak_count(&self) -> Option<usize> {
1850+
/// If no strong pointers remain, this will return zero.
1851+
#[stable(feature = "weak_counts", since = "1.41.0")]
1852+
pub fn weak_count(&self) -> usize {
18571853
self.inner().map(|inner| {
18581854
if inner.strong() > 0 {
18591855
inner.weak() - 1 // subtract the implicit weak ptr
18601856
} else {
1861-
inner.weak()
1857+
0
18621858
}
1863-
})
1859+
}).unwrap_or(0)
18641860
}
18651861

18661862
/// Returns `None` when the pointer is dangling and there is no allocated `RcBox`

src/liballoc/rc/tests.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -114,28 +114,28 @@ fn test_weak_count() {
114114

115115
#[test]
116116
fn weak_counts() {
117-
assert_eq!(Weak::weak_count(&Weak::<u64>::new()), None);
117+
assert_eq!(Weak::weak_count(&Weak::<u64>::new()), 0);
118118
assert_eq!(Weak::strong_count(&Weak::<u64>::new()), 0);
119119

120120
let a = Rc::new(0);
121121
let w = Rc::downgrade(&a);
122122
assert_eq!(Weak::strong_count(&w), 1);
123-
assert_eq!(Weak::weak_count(&w), Some(1));
123+
assert_eq!(Weak::weak_count(&w), 1);
124124
let w2 = w.clone();
125125
assert_eq!(Weak::strong_count(&w), 1);
126-
assert_eq!(Weak::weak_count(&w), Some(2));
126+
assert_eq!(Weak::weak_count(&w), 2);
127127
assert_eq!(Weak::strong_count(&w2), 1);
128-
assert_eq!(Weak::weak_count(&w2), Some(2));
128+
assert_eq!(Weak::weak_count(&w2), 2);
129129
drop(w);
130130
assert_eq!(Weak::strong_count(&w2), 1);
131-
assert_eq!(Weak::weak_count(&w2), Some(1));
131+
assert_eq!(Weak::weak_count(&w2), 1);
132132
let a2 = a.clone();
133133
assert_eq!(Weak::strong_count(&w2), 2);
134-
assert_eq!(Weak::weak_count(&w2), Some(1));
134+
assert_eq!(Weak::weak_count(&w2), 1);
135135
drop(a2);
136136
drop(a);
137137
assert_eq!(Weak::strong_count(&w2), 0);
138-
assert_eq!(Weak::weak_count(&w2), Some(1));
138+
assert_eq!(Weak::weak_count(&w2), 0);
139139
drop(w2);
140140
}
141141

src/liballoc/sync.rs

+15-25
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use core::sync::atomic;
1212
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
1313
use core::borrow;
1414
use core::fmt;
15-
use core::cmp::{self, Ordering};
15+
use core::cmp::Ordering;
1616
use core::iter;
1717
use core::intrinsics::abort;
1818
use core::mem::{self, align_of, align_of_val, size_of_val};
@@ -1529,7 +1529,7 @@ impl<T: ?Sized> Weak<T> {
15291529
/// If `self` was created using [`Weak::new`], this will return 0.
15301530
///
15311531
/// [`Weak::new`]: #method.new
1532-
#[unstable(feature = "weak_counts", issue = "57977")]
1532+
#[stable(feature = "weak_counts", since = "1.41.0")]
15331533
pub fn strong_count(&self) -> usize {
15341534
if let Some(inner) = self.inner() {
15351535
inner.strong.load(SeqCst)
@@ -1541,9 +1541,8 @@ impl<T: ?Sized> Weak<T> {
15411541
/// Gets an approximation of the number of `Weak` pointers pointing to this
15421542
/// allocation.
15431543
///
1544-
/// If `self` was created using [`Weak::new`], this will return 0. If not,
1545-
/// the returned value is at least 1, since `self` still points to the
1546-
/// allocation.
1544+
/// If `self` was created using [`Weak::new`], or if there are no remaining
1545+
/// strong pointers, this will return 0.
15471546
///
15481547
/// # Accuracy
15491548
///
@@ -1552,31 +1551,22 @@ impl<T: ?Sized> Weak<T> {
15521551
/// `Weak`s pointing to the same allocation.
15531552
///
15541553
/// [`Weak::new`]: #method.new
1555-
#[unstable(feature = "weak_counts", issue = "57977")]
1556-
pub fn weak_count(&self) -> Option<usize> {
1557-
// Due to the implicit weak pointer added when any strong pointers are
1558-
// around, we cannot implement `weak_count` correctly since it
1559-
// necessarily requires accessing the strong count and weak count in an
1560-
// unsynchronized fashion. So this version is a bit racy.
1554+
#[stable(feature = "weak_counts", since = "1.41.0")]
1555+
pub fn weak_count(&self) -> usize {
15611556
self.inner().map(|inner| {
1562-
let strong = inner.strong.load(SeqCst);
15631557
let weak = inner.weak.load(SeqCst);
1558+
let strong = inner.strong.load(SeqCst);
15641559
if strong == 0 {
1565-
// If the last `Arc` has *just* been dropped, it might not yet
1566-
// have removed the implicit weak count, so the value we get
1567-
// here might be 1 too high.
1568-
weak
1560+
0
15691561
} else {
1570-
// As long as there's still at least 1 `Arc` around, subtract
1571-
// the implicit weak pointer.
1572-
// Note that the last `Arc` might get dropped between the 2
1573-
// loads we do above, removing the implicit weak pointer. This
1574-
// means that the value might be 1 too low here. In order to not
1575-
// return 0 here (which would happen if we're the only weak
1576-
// pointer), we guard against that specifically.
1577-
cmp::max(1, weak - 1)
1562+
// Since we observed that there was at least one strong pointer
1563+
// after reading the weak count, we know that the implicit weak
1564+
// reference (present whenever any strong references are alive)
1565+
// was still around when we observed the weak count, and can
1566+
// therefore safely subtract it.
1567+
weak - 1
15781568
}
1579-
})
1569+
}).unwrap_or(0)
15801570
}
15811571

15821572
/// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`,

src/liballoc/sync/tests.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -62,28 +62,28 @@ fn test_arc_get_mut() {
6262

6363
#[test]
6464
fn weak_counts() {
65-
assert_eq!(Weak::weak_count(&Weak::<u64>::new()), None);
65+
assert_eq!(Weak::weak_count(&Weak::<u64>::new()), 0);
6666
assert_eq!(Weak::strong_count(&Weak::<u64>::new()), 0);
6767

6868
let a = Arc::new(0);
6969
let w = Arc::downgrade(&a);
7070
assert_eq!(Weak::strong_count(&w), 1);
71-
assert_eq!(Weak::weak_count(&w), Some(1));
71+
assert_eq!(Weak::weak_count(&w), 1);
7272
let w2 = w.clone();
7373
assert_eq!(Weak::strong_count(&w), 1);
74-
assert_eq!(Weak::weak_count(&w), Some(2));
74+
assert_eq!(Weak::weak_count(&w), 2);
7575
assert_eq!(Weak::strong_count(&w2), 1);
76-
assert_eq!(Weak::weak_count(&w2), Some(2));
76+
assert_eq!(Weak::weak_count(&w2), 2);
7777
drop(w);
7878
assert_eq!(Weak::strong_count(&w2), 1);
79-
assert_eq!(Weak::weak_count(&w2), Some(1));
79+
assert_eq!(Weak::weak_count(&w2), 1);
8080
let a2 = a.clone();
8181
assert_eq!(Weak::strong_count(&w2), 2);
82-
assert_eq!(Weak::weak_count(&w2), Some(1));
82+
assert_eq!(Weak::weak_count(&w2), 1);
8383
drop(a2);
8484
drop(a);
8585
assert_eq!(Weak::strong_count(&w2), 0);
86-
assert_eq!(Weak::weak_count(&w2), Some(1));
86+
assert_eq!(Weak::weak_count(&w2), 0);
8787
drop(w2);
8888
}
8989

0 commit comments

Comments
 (0)