Skip to content

Commit 7e0edb3

Browse files
Implement a slightly racy sync::Weak::weak_count
1 parent 2fe3b3b commit 7e0edb3

File tree

1 file changed

+70
-5
lines changed

1 file changed

+70
-5
lines changed

src/liballoc/sync.rs

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use core::sync::atomic;
1111
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
1212
use core::borrow;
1313
use core::fmt;
14-
use core::cmp::Ordering;
14+
use core::cmp::{self, Ordering};
1515
use core::intrinsics::abort;
1616
use core::mem::{self, align_of_val, size_of_val};
1717
use core::ops::{Deref, Receiver};
@@ -1131,10 +1131,48 @@ impl<T: ?Sized> Weak<T> {
11311131
}
11321132
}
11331133

1134-
// Due to the implicit weak pointer added when any strong pointers are
1135-
// around, we cannot implement `weak_count` correctly since it necessarily
1136-
// requires accessing the strong count and weak count in an unsynchronized
1137-
// fashion.
1134+
/// Gets an approximation of the number of `Weak` pointers pointing to this
1135+
/// value.
1136+
///
1137+
/// If `self` was created using [`Weak::new`], this will return 0. If not,
1138+
/// the returned value is at least 1, since `self` still points to the
1139+
/// value.
1140+
///
1141+
/// # Accuracy
1142+
///
1143+
/// Due to implementation details, the returned value can be off by 1 in
1144+
/// either direction when other threads are manipulating any `Arc`s or
1145+
/// `Weak`s pointing to the same value.
1146+
///
1147+
/// [`Weak::new`]: #method.new
1148+
#[unstable(feature = "weak_counts", issue = "0")]
1149+
pub fn weak_count(&self) -> usize {
1150+
// Due to the implicit weak pointer added when any strong pointers are
1151+
// around, we cannot implement `weak_count` correctly since it
1152+
// necessarily requires accessing the strong count and weak count in an
1153+
// unsynchronized fashion. So this version is a bit racy.
1154+
if let Some(inner) = self.inner() {
1155+
let strong = inner.strong.load(SeqCst);
1156+
let weak = inner.weak.load(SeqCst);
1157+
if strong == 0 {
1158+
// If the last `Arc` has *just* been dropped, it might not yet
1159+
// have removed the implicit weak count, so the value we get
1160+
// here might be 1 too high.
1161+
weak
1162+
} else {
1163+
// As long as there's still at least 1 `Arc` around, subtract
1164+
// the implicit weak pointer.
1165+
// Note that the last `Arc` might get dropped between the 2
1166+
// loads we do above, removing the implicit weak pointer. This
1167+
// means that the value might be 1 too low here. In order to not
1168+
// return 0 here (which would happen if we're the only weak
1169+
// pointer), we guard against that specifically.
1170+
cmp::max(1, weak - 1)
1171+
}
1172+
} else {
1173+
0
1174+
}
1175+
}
11381176

11391177
/// Return `None` when the pointer is dangling and there is no allocated `ArcInner`,
11401178
/// i.e., this `Weak` was created by `Weak::new`
@@ -1655,6 +1693,33 @@ mod tests {
16551693
assert!(Arc::get_mut(&mut x).is_none());
16561694
}
16571695

1696+
#[test]
1697+
fn weak_counts() {
1698+
assert_eq!(Weak::weak_count(&Weak::<u64>::new()), 0);
1699+
assert_eq!(Weak::strong_count(&Weak::<u64>::new()), 0);
1700+
1701+
let a = Arc::new(0);
1702+
let w = Arc::downgrade(&a);
1703+
assert_eq!(Weak::strong_count(&w), 1);
1704+
assert_eq!(Weak::weak_count(&w), 1);
1705+
let w2 = w.clone();
1706+
assert_eq!(Weak::strong_count(&w), 1);
1707+
assert_eq!(Weak::weak_count(&w), 2);
1708+
assert_eq!(Weak::strong_count(&w2), 1);
1709+
assert_eq!(Weak::weak_count(&w2), 2);
1710+
drop(w);
1711+
assert_eq!(Weak::strong_count(&w2), 1);
1712+
assert_eq!(Weak::weak_count(&w2), 1);
1713+
let a2 = a.clone();
1714+
assert_eq!(Weak::strong_count(&w2), 2);
1715+
assert_eq!(Weak::weak_count(&w2), 1);
1716+
drop(a2);
1717+
drop(a);
1718+
assert_eq!(Weak::strong_count(&w2), 0);
1719+
assert_eq!(Weak::weak_count(&w2), 1);
1720+
drop(w2);
1721+
}
1722+
16581723
#[test]
16591724
fn try_unwrap() {
16601725
let x = Arc::new(3);

0 commit comments

Comments
 (0)