diff options
| author | Jonas Schievink <jonasschievink@gmail.com> | 2019-01-07 23:35:52 +0100 |
|---|---|---|
| committer | Jonas Schievink <jonasschievink@gmail.com> | 2019-01-29 21:07:35 +0100 |
| commit | 7e0edb39baa02c58c373f05014ffd8e3058c93ad (patch) | |
| tree | 52f21d65ed37913eee12252ec4d005a97929ab84 /src/liballoc | |
| parent | 2fe3b3b486b3b5e1ec1e6ad259d8840b88b7c77a (diff) | |
| download | rust-7e0edb39baa02c58c373f05014ffd8e3058c93ad.tar.gz rust-7e0edb39baa02c58c373f05014ffd8e3058c93ad.zip | |
Implement a slightly racy `sync::Weak::weak_count`
Diffstat (limited to 'src/liballoc')
| -rw-r--r-- | src/liballoc/sync.rs | 75 |
1 files changed, 70 insertions, 5 deletions
diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index fd3519dee18..b065bd60c60 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<T: ?Sized> Weak<T> { } } - // 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` @@ -1656,6 +1694,33 @@ mod tests { } #[test] + fn weak_counts() { + assert_eq!(Weak::weak_count(&Weak::<u64>::new()), 0); + assert_eq!(Weak::strong_count(&Weak::<u64>::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); assert_eq!(Arc::try_unwrap(x), Ok(3)); |
