focus_chain_provider/
instance_counter.rs

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.
4
5use std::sync::atomic::{AtomicUsize, Ordering};
6use std::sync::Arc;
7
8/// 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}
17
18impl InstanceCounter {
19    /// Creates a new counter with an initial count of 0.
20    pub fn new() -> Self {
21        Self { inner: Arc::new(0.into()) }
22    }
23
24    /// Creates a new token and increments the count by 1. When the token is dropped, the count will
25    /// decrement by 1.
26    pub fn make_token(&self) -> CountingToken {
27        let token = CountingToken { inner: self.inner.clone() };
28        self.inner.fetch_add(1, Ordering::SeqCst);
29        token
30    }
31
32    /// Returns the number of outstanding tokens.
33    pub fn count(&self) -> usize {
34        self.inner.load(Ordering::SeqCst)
35    }
36}
37
38/// See [`InstanceCounter::make_token`].
39#[derive(Debug)]
40pub(crate) struct CountingToken {
41    inner: Arc<AtomicUsize>,
42}
43
44impl Drop for CountingToken {
45    fn drop(&mut self) {
46        self.inner.fetch_sub(1, Ordering::SeqCst);
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::InstanceCounter;
53    #[test]
54    fn smoke_test() {
55        let counter = InstanceCounter::new();
56        assert_eq!(counter.count(), 0);
57
58        let token_a = counter.make_token();
59        assert_eq!(counter.count(), 1);
60
61        let clone = counter.clone();
62        assert_eq!(counter.count(), 1);
63        assert_eq!(clone.count(), 1);
64
65        let token_b = counter.make_token();
66        assert_eq!(counter.count(), 2);
67        assert_eq!(clone.count(), 2);
68
69        drop(clone);
70        assert_eq!(counter.count(), 2);
71
72        drop(token_a);
73        assert_eq!(counter.count(), 1);
74
75        drop(token_b);
76        assert_eq!(counter.count(), 0);
77    }
78}