about summary refs log tree commit diff
path: root/src/liballoc
diff options
context:
space:
mode:
authorAlexander Light <scialexlight@gmail.com>2014-11-20 16:38:36 -0500
committerAlexander Light <scialexlight@gmail.com>2014-11-21 15:42:01 -0500
commit4c36ad01e709448e8090b79ac96d0a6bb0607b23 (patch)
treecd77f0dbc10914aa365355a656916d455e121eee /src/liballoc
parent96c8f2b0c1846756e617f1f1fc1372c506e24248 (diff)
downloadrust-4c36ad01e709448e8090b79ac96d0a6bb0607b23.tar.gz
rust-4c36ad01e709448e8090b79ac96d0a6bb0607b23.zip
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.
Diffstat (limited to 'src/liballoc')
-rw-r--r--src/liballoc/arc.rs74
-rw-r--r--src/liballoc/rc.rs61
2 files changed, 133 insertions, 2 deletions
diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs
index c4f53d74467..2a087fa678c 100644
--- a/src/liballoc/arc.rs
+++ b/src/liballoc/arc.rs
@@ -117,6 +117,16 @@ impl<T> Arc<T> {
         // 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<T: Sync + Send> Weak<T> {
         // 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."]
@@ -466,6 +499,47 @@ mod tests {
     }
 
     #[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);
         assert!(format!("{}", a).as_slice() == "5")
diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs
index 501f915461a..ba6f5cde2f4 100644
--- a/src/liballoc/rc.rs
+++ b/src/liballoc/rc.rs
@@ -211,6 +211,16 @@ impl<T> Rc<T> {
             _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<T> Rc<T> {
 #[inline]
 #[experimental]
 pub fn is_unique<T>(rc: &Rc<T>) -> 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<T> Weak<T> {
             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]
@@ -567,6 +590,40 @@ mod tests {
     }
 
     #[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);
         assert_eq!(super::try_unwrap(x), Ok(3u));