From 4c36ad01e709448e8090b79ac96d0a6bb0607b23 Mon Sep 17 00:00:00 2001 From: Alexander Light Date: Thu, 20 Nov 2014 16:38:36 -0500 Subject: [PATCH 1/2] Add `weak_count` and `strong_count` to Rc and Arc These functions allow you to see how many weak and strong references there are to an `Arc`, `Rc`, or an `rc::Weak`. Due to the design of `Arc` it is not possible to get the number of weak references of an arbitrary `arc::Weak`. Look in `arc.rs` for a more in-depth explanation. On `arc::Arc` and `arc::Weak` these operations are wait-free and atomic. --- src/liballoc/arc.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++ src/liballoc/rc.rs | 61 +++++++++++++++++++++++++++++++++++-- 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index c4f53d744673f..2a087fa678c90 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -117,6 +117,16 @@ impl Arc { // these contents. unsafe { &*self._ptr } } + + /// Get the number of weak references to this value. + #[inline] + #[experimental] + pub fn weak_count(&self) -> uint { self.inner().weak.load(atomic::SeqCst) - 1 } + + /// Get the number of strong references to this value. + #[inline] + #[experimental] + pub fn strong_count(&self) -> uint { self.inner().strong.load(atomic::SeqCst) } } #[unstable = "waiting on stability of Clone"] @@ -247,6 +257,29 @@ impl Weak { // See comments above for why this is "safe" unsafe { &*self._ptr } } + + // Why is there no `weak_count()`? + // + // It is not possible to determine the number of weak references with only a weak reference + // accurately in a wait-free manner. This is because we have a data-race with the last strong + // reference's `drop` method. If that operation pauses between decrementing the strong + // reference count to 0 and removing the implicit weak reference that the strong references + // share then we will incorrectly think there is one more weak reference then there really is. + // + // We cannot get around this without making parts of this object no longer wait-free, since we + // would either need to use locks to get mutual exclusion with `drop` or make it so that the + // weak and strong reference counts can be modified atomically together. The first option + // destroys wait-freedom by adding a lock and the second (in addition to being annoying to + // implement) would make many operations (at least `downgrade` and both `clone`s) go from being + // wait-free to merely lock-free, as we would need to do a manual CAS loop to get around other + // threads modifying the other value in each of these cases. + + /// Get the number of strong references to this value. + /// + /// If this function returns 0 then the value has been freed. + #[inline] + #[experimental] + pub fn strong_count(&self) -> uint { self.inner().strong.load(atomic::SeqCst) } } #[experimental = "Weak pointers may not belong in this module."] @@ -465,6 +498,47 @@ mod tests { drop(arc_weak); } + #[test] + fn test_strong_count() { + let a = Arc::new(0u32); + assert!(a.strong_count() == 1); + let w = a.downgrade(); + assert!(a.strong_count() == 1); + let b = w.upgrade().expect(""); + assert!(b.strong_count() == 2); + assert!(a.strong_count() == 2); + drop(w); + drop(a); + assert!(b.strong_count() == 1); + let c = b.clone(); + assert!(b.strong_count() == 2); + assert!(c.strong_count() == 2); + } + + #[test] + fn test_weak_count() { + let a = Arc::new(0u32); + assert!(a.strong_count() == 1); + assert!(a.weak_count() == 0); + let w = a.downgrade(); + assert!(a.strong_count() == 1); + assert!(w.strong_count() == 1); + assert!(a.weak_count() == 1); + drop(w); + assert!(a.strong_count() == 1); + assert!(a.weak_count() == 0); + let c = a.clone(); + assert!(a.strong_count() == 2); + assert!(a.weak_count() == 0); + let d = c.downgrade(); + assert!(c.weak_count() == 1); + assert!(c.strong_count() == 2); + + drop(a); + drop(c); + drop(d); + } + #[test] fn show_arc() { let a = Arc::new(5u32); diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 501f915461a27..ba6f5cde2f49b 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -211,6 +211,16 @@ impl Rc { _noshare: marker::NoSync } } + + /// Get the number of weak references to this value. + #[inline] + #[experimental] + pub fn weak_count(&self) -> uint { self.weak() - 1 } + + /// Get the number of strong references to this value. + #[inline] + #[experimental] + pub fn strong_count(&self) -> uint { self.strong() } } /// Returns true if the `Rc` currently has unique ownership. @@ -220,8 +230,7 @@ impl Rc { #[inline] #[experimental] pub fn is_unique(rc: &Rc) -> bool { - // note that we hold both a strong and a weak reference - rc.strong() == 1 && rc.weak() == 1 + rc.weak_count() == 0 && rc.strong_count() == 1 } /// Unwraps the contained value if the `Rc` has unique ownership. @@ -424,6 +433,20 @@ impl Weak { Some(Rc { _ptr: self._ptr, _nosend: marker::NoSend, _noshare: marker::NoSync }) } } + + /// Get the number of weak references to this value. + #[inline] + #[experimental] + pub fn weak_count(&self) -> uint { + if self.strong() != 0 { self.weak() - 1 } else { self.weak() } + } + + /// Get the number of strong references to this value. + /// + /// If this function returns 0 then the value has been freed. + #[inline] + #[experimental] + pub fn strong_count(&self) -> uint { self.strong() } } #[unsafe_destructor] @@ -566,6 +589,40 @@ mod tests { assert!(super::is_unique(&x)); } + #[test] + fn test_strong_count() { + let a = Rc::new(0u32); + assert!(a.strong_count() == 1); + let w = a.downgrade(); + assert!(a.strong_count() == 1); + let b = w.upgrade().expect("upgrade of live rc failed"); + assert!(b.strong_count() == 2); + assert!(a.strong_count() == 2); + drop(w); + drop(a); + assert!(b.strong_count() == 1); + let c = b.clone(); + assert!(b.strong_count() == 2); + assert!(c.strong_count() == 2); + } + + #[test] + fn test_weak_count() { + let a = Rc::new(0u32); + assert!(a.strong_count() == 1); + assert!(a.weak_count() == 0); + let w = a.downgrade(); + assert!(a.strong_count() == 1); + assert!(w.weak_count() == 1); + drop(w); + assert!(a.strong_count() == 1); + assert!(a.weak_count() == 0); + let c = a.clone(); + assert!(a.strong_count() == 2); + assert!(a.weak_count() == 0); + assert!(c.downgrade().weak_count() == 1); + } + #[test] fn try_unwrap() { let x = Rc::new(3u); From 69861df831a2d0c551b46a291c2530aeeab6c704 Mon Sep 17 00:00:00 2001 From: Alexander Light Date: Fri, 21 Nov 2014 17:56:33 -0500 Subject: [PATCH 2/2] Shuffle locations for Deref Remove both `strong_count` and `weak_count` from `Weak`s and make the methods bare functions so as not to cause trouble with `deref`. --- src/liballoc/arc.rs | 81 +++++++++++++++++---------------------------- src/liballoc/rc.rs | 68 +++++++++++++++---------------------- 2 files changed, 57 insertions(+), 92 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 2a087fa678c90..4f744b0b2dee1 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -117,17 +117,17 @@ impl Arc { // these contents. unsafe { &*self._ptr } } +} - /// Get the number of weak references to this value. - #[inline] - #[experimental] - pub fn weak_count(&self) -> uint { self.inner().weak.load(atomic::SeqCst) - 1 } +/// Get the number of weak references to this value. +#[inline] +#[experimental] +pub fn weak_count(this: &Arc) -> uint { this.inner().weak.load(atomic::SeqCst) - 1 } - /// Get the number of strong references to this value. - #[inline] - #[experimental] - pub fn strong_count(&self) -> uint { self.inner().strong.load(atomic::SeqCst) } -} +/// Get the number of strong references to this value. +#[inline] +#[experimental] +pub fn strong_count(this: &Arc) -> uint { this.inner().strong.load(atomic::SeqCst) } #[unstable = "waiting on stability of Clone"] impl Clone for Arc { @@ -257,29 +257,6 @@ impl Weak { // See comments above for why this is "safe" unsafe { &*self._ptr } } - - // Why is there no `weak_count()`? - // - // It is not possible to determine the number of weak references with only a weak reference - // accurately in a wait-free manner. This is because we have a data-race with the last strong - // reference's `drop` method. If that operation pauses between decrementing the strong - // reference count to 0 and removing the implicit weak reference that the strong references - // share then we will incorrectly think there is one more weak reference then there really is. - // - // We cannot get around this without making parts of this object no longer wait-free, since we - // would either need to use locks to get mutual exclusion with `drop` or make it so that the - // weak and strong reference counts can be modified atomically together. The first option - // destroys wait-freedom by adding a lock and the second (in addition to being annoying to - // implement) would make many operations (at least `downgrade` and both `clone`s) go from being - // wait-free to merely lock-free, as we would need to do a manual CAS loop to get around other - // threads modifying the other value in each of these cases. - - /// Get the number of strong references to this value. - /// - /// If this function returns 0 then the value has been freed. - #[inline] - #[experimental] - pub fn strong_count(&self) -> uint { self.inner().strong.load(atomic::SeqCst) } } #[experimental = "Weak pointers may not belong in this module."] @@ -354,7 +331,7 @@ mod tests { use std::sync::atomic; use std::task; use std::vec::Vec; - use super::{Arc, Weak}; + use super::{Arc, Weak, weak_count, strong_count}; use std::sync::Mutex; struct Canary(*mut atomic::AtomicUint); @@ -501,38 +478,40 @@ mod tests { #[test] fn test_strong_count() { let a = Arc::new(0u32); - assert!(a.strong_count() == 1); + assert!(strong_count(&a) == 1); let w = a.downgrade(); - assert!(a.strong_count() == 1); + assert!(strong_count(&a) == 1); let b = w.upgrade().expect(""); - assert!(b.strong_count() == 2); - assert!(a.strong_count() == 2); + assert!(strong_count(&b) == 2); + assert!(strong_count(&a) == 2); drop(w); drop(a); - assert!(b.strong_count() == 1); + assert!(strong_count(&b) == 1); let c = b.clone(); - assert!(b.strong_count() == 2); - assert!(c.strong_count() == 2); + assert!(strong_count(&b) == 2); + assert!(strong_count(&c) == 2); } #[test] fn test_weak_count() { let a = Arc::new(0u32); - assert!(a.strong_count() == 1); - assert!(a.weak_count() == 0); + assert!(strong_count(&a) == 1); + assert!(weak_count(&a) == 0); let w = a.downgrade(); - assert!(a.strong_count() == 1); - assert!(w.strong_count() == 1); - assert!(a.weak_count() == 1); + assert!(strong_count(&a) == 1); + assert!(weak_count(&a) == 1); + let x = w.clone(); + assert!(weak_count(&a) == 2); drop(w); - assert!(a.strong_count() == 1); - assert!(a.weak_count() == 0); + drop(x); + assert!(strong_count(&a) == 1); + assert!(weak_count(&a) == 0); let c = a.clone(); - assert!(a.strong_count() == 2); - assert!(a.weak_count() == 0); + assert!(strong_count(&a) == 2); + assert!(weak_count(&a) == 0); let d = c.downgrade(); - assert!(c.weak_count() == 1); - assert!(c.strong_count() == 2); + assert!(weak_count(&c) == 1); + assert!(strong_count(&c) == 2); drop(a); drop(c); diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index ba6f5cde2f49b..df84ac9aec935 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -211,17 +211,17 @@ impl Rc { _noshare: marker::NoSync } } +} - /// Get the number of weak references to this value. - #[inline] - #[experimental] - pub fn weak_count(&self) -> uint { self.weak() - 1 } +/// Get the number of weak references to this value. +#[inline] +#[experimental] +pub fn weak_count(this: &Rc) -> uint { this.weak() - 1 } - /// Get the number of strong references to this value. - #[inline] - #[experimental] - pub fn strong_count(&self) -> uint { self.strong() } -} +/// Get the number of strong references to this value. +#[inline] +#[experimental] +pub fn strong_count(this: &Rc) -> uint { this.strong() } /// Returns true if the `Rc` currently has unique ownership. /// @@ -230,7 +230,7 @@ impl Rc { #[inline] #[experimental] pub fn is_unique(rc: &Rc) -> bool { - rc.weak_count() == 0 && rc.strong_count() == 1 + weak_count(rc) == 0 && strong_count(rc) == 1 } /// Unwraps the contained value if the `Rc` has unique ownership. @@ -433,20 +433,6 @@ impl Weak { Some(Rc { _ptr: self._ptr, _nosend: marker::NoSend, _noshare: marker::NoSync }) } } - - /// Get the number of weak references to this value. - #[inline] - #[experimental] - pub fn weak_count(&self) -> uint { - if self.strong() != 0 { self.weak() - 1 } else { self.weak() } - } - - /// Get the number of strong references to this value. - /// - /// If this function returns 0 then the value has been freed. - #[inline] - #[experimental] - pub fn strong_count(&self) -> uint { self.strong() } } #[unsafe_destructor] @@ -512,7 +498,7 @@ impl RcBoxPtr for Weak { #[cfg(test)] #[allow(experimental)] mod tests { - use super::{Rc, Weak}; + use super::{Rc, Weak, weak_count, strong_count}; use std::cell::RefCell; use std::option::{Option, Some, None}; use std::result::{Err, Ok}; @@ -592,35 +578,35 @@ mod tests { #[test] fn test_strong_count() { let a = Rc::new(0u32); - assert!(a.strong_count() == 1); + assert!(strong_count(&a) == 1); let w = a.downgrade(); - assert!(a.strong_count() == 1); + assert!(strong_count(&a) == 1); let b = w.upgrade().expect("upgrade of live rc failed"); - assert!(b.strong_count() == 2); - assert!(a.strong_count() == 2); + assert!(strong_count(&b) == 2); + assert!(strong_count(&a) == 2); drop(w); drop(a); - assert!(b.strong_count() == 1); + assert!(strong_count(&b) == 1); let c = b.clone(); - assert!(b.strong_count() == 2); - assert!(c.strong_count() == 2); + assert!(strong_count(&b) == 2); + assert!(strong_count(&c) == 2); } #[test] fn test_weak_count() { let a = Rc::new(0u32); - assert!(a.strong_count() == 1); - assert!(a.weak_count() == 0); + assert!(strong_count(&a) == 1); + assert!(weak_count(&a) == 0); let w = a.downgrade(); - assert!(a.strong_count() == 1); - assert!(w.weak_count() == 1); + assert!(strong_count(&a) == 1); + assert!(weak_count(&a) == 1); drop(w); - assert!(a.strong_count() == 1); - assert!(a.weak_count() == 0); + assert!(strong_count(&a) == 1); + assert!(weak_count(&a) == 0); let c = a.clone(); - assert!(a.strong_count() == 2); - assert!(a.weak_count() == 0); - assert!(c.downgrade().weak_count() == 1); + assert!(strong_count(&a) == 2); + assert!(weak_count(&a) == 0); + drop(c); } #[test]