Skip to content

Commit 4c36ad0

Browse files
committed
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.
1 parent 96c8f2b commit 4c36ad0

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

src/liballoc/arc.rs

+74
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ impl<T> Arc<T> {
117117
// these contents.
118118
unsafe { &*self._ptr }
119119
}
120+
121+
/// Get the number of weak references to this value.
122+
#[inline]
123+
#[experimental]
124+
pub fn weak_count(&self) -> uint { self.inner().weak.load(atomic::SeqCst) - 1 }
125+
126+
/// Get the number of strong references to this value.
127+
#[inline]
128+
#[experimental]
129+
pub fn strong_count(&self) -> uint { self.inner().strong.load(atomic::SeqCst) }
120130
}
121131

122132
#[unstable = "waiting on stability of Clone"]
@@ -247,6 +257,29 @@ impl<T: Sync + Send> Weak<T> {
247257
// See comments above for why this is "safe"
248258
unsafe { &*self._ptr }
249259
}
260+
261+
// Why is there no `weak_count()`?
262+
//
263+
// It is not possible to determine the number of weak references with only a weak reference
264+
// accurately in a wait-free manner. This is because we have a data-race with the last strong
265+
// reference's `drop` method. If that operation pauses between decrementing the strong
266+
// reference count to 0 and removing the implicit weak reference that the strong references
267+
// share then we will incorrectly think there is one more weak reference then there really is.
268+
//
269+
// We cannot get around this without making parts of this object no longer wait-free, since we
270+
// would either need to use locks to get mutual exclusion with `drop` or make it so that the
271+
// weak and strong reference counts can be modified atomically together. The first option
272+
// destroys wait-freedom by adding a lock and the second (in addition to being annoying to
273+
// implement) would make many operations (at least `downgrade` and both `clone`s) go from being
274+
// wait-free to merely lock-free, as we would need to do a manual CAS loop to get around other
275+
// threads modifying the other value in each of these cases.
276+
277+
/// Get the number of strong references to this value.
278+
///
279+
/// If this function returns 0 then the value has been freed.
280+
#[inline]
281+
#[experimental]
282+
pub fn strong_count(&self) -> uint { self.inner().strong.load(atomic::SeqCst) }
250283
}
251284

252285
#[experimental = "Weak pointers may not belong in this module."]
@@ -465,6 +498,47 @@ mod tests {
465498
drop(arc_weak);
466499
}
467500

501+
#[test]
502+
fn test_strong_count() {
503+
let a = Arc::new(0u32);
504+
assert!(a.strong_count() == 1);
505+
let w = a.downgrade();
506+
assert!(a.strong_count() == 1);
507+
let b = w.upgrade().expect("");
508+
assert!(b.strong_count() == 2);
509+
assert!(a.strong_count() == 2);
510+
drop(w);
511+
drop(a);
512+
assert!(b.strong_count() == 1);
513+
let c = b.clone();
514+
assert!(b.strong_count() == 2);
515+
assert!(c.strong_count() == 2);
516+
}
517+
518+
#[test]
519+
fn test_weak_count() {
520+
let a = Arc::new(0u32);
521+
assert!(a.strong_count() == 1);
522+
assert!(a.weak_count() == 0);
523+
let w = a.downgrade();
524+
assert!(a.strong_count() == 1);
525+
assert!(w.strong_count() == 1);
526+
assert!(a.weak_count() == 1);
527+
drop(w);
528+
assert!(a.strong_count() == 1);
529+
assert!(a.weak_count() == 0);
530+
let c = a.clone();
531+
assert!(a.strong_count() == 2);
532+
assert!(a.weak_count() == 0);
533+
let d = c.downgrade();
534+
assert!(c.weak_count() == 1);
535+
assert!(c.strong_count() == 2);
536+
537+
drop(a);
538+
drop(c);
539+
drop(d);
540+
}
541+
468542
#[test]
469543
fn show_arc() {
470544
let a = Arc::new(5u32);

src/liballoc/rc.rs

+59-2
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,16 @@ impl<T> Rc<T> {
211211
_noshare: marker::NoSync
212212
}
213213
}
214+
215+
/// Get the number of weak references to this value.
216+
#[inline]
217+
#[experimental]
218+
pub fn weak_count(&self) -> uint { self.weak() - 1 }
219+
220+
/// Get the number of strong references to this value.
221+
#[inline]
222+
#[experimental]
223+
pub fn strong_count(&self) -> uint { self.strong() }
214224
}
215225

216226
/// Returns true if the `Rc` currently has unique ownership.
@@ -220,8 +230,7 @@ impl<T> Rc<T> {
220230
#[inline]
221231
#[experimental]
222232
pub fn is_unique<T>(rc: &Rc<T>) -> bool {
223-
// note that we hold both a strong and a weak reference
224-
rc.strong() == 1 && rc.weak() == 1
233+
rc.weak_count() == 0 && rc.strong_count() == 1
225234
}
226235

227236
/// Unwraps the contained value if the `Rc` has unique ownership.
@@ -424,6 +433,20 @@ impl<T> Weak<T> {
424433
Some(Rc { _ptr: self._ptr, _nosend: marker::NoSend, _noshare: marker::NoSync })
425434
}
426435
}
436+
437+
/// Get the number of weak references to this value.
438+
#[inline]
439+
#[experimental]
440+
pub fn weak_count(&self) -> uint {
441+
if self.strong() != 0 { self.weak() - 1 } else { self.weak() }
442+
}
443+
444+
/// Get the number of strong references to this value.
445+
///
446+
/// If this function returns 0 then the value has been freed.
447+
#[inline]
448+
#[experimental]
449+
pub fn strong_count(&self) -> uint { self.strong() }
427450
}
428451

429452
#[unsafe_destructor]
@@ -566,6 +589,40 @@ mod tests {
566589
assert!(super::is_unique(&x));
567590
}
568591

592+
#[test]
593+
fn test_strong_count() {
594+
let a = Rc::new(0u32);
595+
assert!(a.strong_count() == 1);
596+
let w = a.downgrade();
597+
assert!(a.strong_count() == 1);
598+
let b = w.upgrade().expect("upgrade of live rc failed");
599+
assert!(b.strong_count() == 2);
600+
assert!(a.strong_count() == 2);
601+
drop(w);
602+
drop(a);
603+
assert!(b.strong_count() == 1);
604+
let c = b.clone();
605+
assert!(b.strong_count() == 2);
606+
assert!(c.strong_count() == 2);
607+
}
608+
609+
#[test]
610+
fn test_weak_count() {
611+
let a = Rc::new(0u32);
612+
assert!(a.strong_count() == 1);
613+
assert!(a.weak_count() == 0);
614+
let w = a.downgrade();
615+
assert!(a.strong_count() == 1);
616+
assert!(w.weak_count() == 1);
617+
drop(w);
618+
assert!(a.strong_count() == 1);
619+
assert!(a.weak_count() == 0);
620+
let c = a.clone();
621+
assert!(a.strong_count() == 2);
622+
assert!(a.weak_count() == 0);
623+
assert!(c.downgrade().weak_count() == 1);
624+
}
625+
569626
#[test]
570627
fn try_unwrap() {
571628
let x = Rc::new(3u);

0 commit comments

Comments
 (0)