1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45use std::sync::atomic::{AtomicUsize, Ordering};
6use std::sync::Arc;
78/// A utility for counting instances of structs or closures, with tokens that increment the count
9/// when instantiated and decrement it when `Drop`ped.
10///
11/// The advantage of this over simply using `Arc::strong_count` is that the owning handle (the
12/// `InstanceCounter`) can also be cloned freely without contributing to the tally.
13#[derive(Debug, Clone)]
14pub(crate) struct InstanceCounter {
15 inner: Arc<AtomicUsize>,
16}
1718impl InstanceCounter {
19/// Creates a new counter with an initial count of 0.
20pub fn new() -> Self {
21Self { inner: Arc::new(0.into()) }
22 }
2324/// Creates a new token and increments the count by 1. When the token is dropped, the count will
25 /// decrement by 1.
26pub fn make_token(&self) -> CountingToken {
27let token = CountingToken { inner: self.inner.clone() };
28self.inner.fetch_add(1, Ordering::SeqCst);
29 token
30 }
3132/// Returns the number of outstanding tokens.
33pub fn count(&self) -> usize {
34self.inner.load(Ordering::SeqCst)
35 }
36}
3738/// See [`InstanceCounter::make_token`].
39#[derive(Debug)]
40pub(crate) struct CountingToken {
41 inner: Arc<AtomicUsize>,
42}
4344impl Drop for CountingToken {
45fn drop(&mut self) {
46self.inner.fetch_sub(1, Ordering::SeqCst);
47 }
48}
4950#[cfg(test)]
51mod tests {
52use super::InstanceCounter;
53#[test]
54fn smoke_test() {
55let counter = InstanceCounter::new();
56assert_eq!(counter.count(), 0);
5758let token_a = counter.make_token();
59assert_eq!(counter.count(), 1);
6061let clone = counter.clone();
62assert_eq!(counter.count(), 1);
63assert_eq!(clone.count(), 1);
6465let token_b = counter.make_token();
66assert_eq!(counter.count(), 2);
67assert_eq!(clone.count(), 2);
6869 drop(clone);
70assert_eq!(counter.count(), 2);
7172 drop(token_a);
73assert_eq!(counter.count(), 1);
7475 drop(token_b);
76assert_eq!(counter.count(), 0);
77 }
78}