netstack3_base/
counters.rs

1// Copyright 2024 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
5//! Common counter abstractions.
6
7use core::fmt::Debug;
8use core::sync::atomic::{AtomicU64, Ordering};
9
10use crate::test_only::TestOnlyPartialEq;
11
12/// An atomic counter for packet statistics, e.g. IPv4 packets received.
13#[derive(Debug, Default)]
14pub struct Counter(AtomicU64);
15
16impl Counter {
17    /// Increments the counter value by 1.
18    pub fn increment(&self) {
19        // Use relaxed ordering since we do not use packet counter values to
20        // synchronize other accesses.  See:
21        // https://doc.rust-lang.org/nomicon/atomics.html#relaxed
22        let Self(v) = self;
23        let _: u64 = v.fetch_add(1, Ordering::Relaxed);
24    }
25
26    /// Adds the provided value to the counter.
27    pub fn add(&self, n: u64) {
28        // Use relaxed ordering since we do not use packet counter values to
29        // synchronize other accesses.  See:
30        // https://doc.rust-lang.org/nomicon/atomics.html#relaxed
31        let Self(v) = self;
32        let _: u64 = v.fetch_add(n, Ordering::Relaxed);
33    }
34
35    /// Atomically retrieves the counter value as a `u64`.
36    pub fn get(&self) -> u64 {
37        // Use relaxed ordering since we do not use packet counter values to
38        // synchronize other accesses.  See:
39        // https://doc.rust-lang.org/nomicon/atomics.html#relaxed
40        let Self(v) = self;
41        v.load(Ordering::Relaxed)
42    }
43}
44
45/// A context that stores counters.
46///
47/// `CounterContext` exposes access to counters for observation and debugging.
48pub trait CounterContext<T> {
49    /// Returns a reference to the counters.
50    fn counters(&self) -> &T;
51}
52
53/// A context that provides access to per-resource counters for observation and
54/// debugging.
55pub trait ResourceCounterContext<R, T>: CounterContext<T> {
56    /// Returns a reference to the set of counters on `resource`.
57    fn per_resource_counters<'a>(&'a self, resource: &'a R) -> &'a T;
58
59    /// Increments both the per-resource and stackwide versions of
60    /// the counter returned by the callback.
61    fn increment_both<F: Fn(&T) -> &Counter>(&self, resource: &R, cb: F) {
62        cb(self.per_resource_counters(resource)).increment();
63        cb(self.counters()).increment();
64    }
65}
66
67mod sealed {
68    use super::*;
69
70    /// Used to implement the "sealed trait" pattern.
71    pub trait Sealed {}
72
73    impl Sealed for u64 {}
74    impl Sealed for Counter {}
75}
76
77/// A marker trait to indicate types that may be used as a counter.
78pub trait CounterRepr: sealed::Sealed + Default + Debug + TestOnlyPartialEq {
79    /// Get the held counter value.
80    fn get(&self) -> u64;
81    /// Construct a new counter from the value.
82    fn new(value: u64) -> Self;
83
84    /// Convert one `CounterRepr` into another `CounterRepr`.
85    fn into_repr<C: CounterRepr>(&self) -> C {
86        C::new(self.get())
87    }
88}
89
90impl CounterRepr for Counter {
91    fn get(&self) -> u64 {
92        self.get()
93    }
94
95    fn new(value: u64) -> Self {
96        Counter(AtomicU64::new(value))
97    }
98}
99
100// Only allow `u64` as a counter in tests.
101#[cfg(any(test, feature = "testutils"))]
102impl CounterRepr for u64 {
103    fn get(&self) -> u64 {
104        *self
105    }
106
107    fn new(value: u64) -> Self {
108        value
109    }
110}