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    /// Adds `value` to both the per-resource and stackwide versions of
67    /// the counter returned by the callback.
68    fn add_both<F: Fn(&T) -> &Counter>(&self, resource: &R, value: u64, cb: F) {
69        cb(self.per_resource_counters(resource)).add(value);
70        cb(self.counters()).add(value);
71    }
72
73    /// Like `add_both` but takes a `usize` value, saturating to `u64::max` if
74    /// conversion fails.
75    fn add_both_usize<F: Fn(&T) -> &Counter>(&self, resource: &R, value: usize, cb: F) {
76        self.add_both(resource, value.try_into().unwrap_or(u64::MAX), cb);
77    }
78}
79
80mod sealed {
81    use super::*;
82
83    /// Used to implement the "sealed trait" pattern.
84    pub trait Sealed {}
85
86    impl Sealed for u64 {}
87    impl Sealed for Counter {}
88}
89
90/// A marker trait to indicate types that may be used as a counter.
91pub trait CounterRepr: sealed::Sealed + Default + Debug + TestOnlyPartialEq {
92    /// Get the held counter value.
93    fn get(&self) -> u64;
94    /// Construct a new counter from the value.
95    fn new(value: u64) -> Self;
96
97    /// Convert one `CounterRepr` into another `CounterRepr`.
98    fn cast<C: CounterRepr>(&self) -> C {
99        C::new(self.get())
100    }
101}
102
103impl CounterRepr for Counter {
104    fn get(&self) -> u64 {
105        self.get()
106    }
107
108    fn new(value: u64) -> Self {
109        Counter(AtomicU64::new(value))
110    }
111}
112
113// Only allow `u64` as a counter in tests.
114#[cfg(any(test, feature = "testutils"))]
115impl CounterRepr for u64 {
116    fn get(&self) -> u64 {
117        *self
118    }
119
120    fn new(value: u64) -> Self {
121        value
122    }
123}
124
125/// The specification of a [`CounterCollection`].
126pub trait CounterCollectionSpec {
127    /// The underlying [`CounterCollection`] for this specification.
128    type CounterCollection<C: CounterRepr>: CounterCollection<Spec = Self>;
129    /// A utility method to transform the underlying counter representation
130    /// within [`Self::CounterCollection`].
131    fn transform<C1: CounterRepr, C2: CounterRepr>(
132        counters: &Self::CounterCollection<C1>,
133    ) -> Self::CounterCollection<C2>;
134}
135
136/// A collection of counters.
137pub trait CounterCollection: Debug + Default + TestOnlyPartialEq {
138    /// The [`CounterCollectionSpec`] associated with this collection.
139    type Spec: CounterCollectionSpec<CounterCollection<Self::Repr> = Self>;
140    /// The counter representation held by this collection.
141    type Repr: CounterRepr;
142    /// A utility method to cast this collection of counters into a different
143    /// underlying counter representation.
144    fn cast<C: CounterRepr>(&self) -> <Self::Spec as CounterCollectionSpec>::CounterCollection<C> {
145        <Self::Spec as CounterCollectionSpec>::transform::<Self::Repr, C>(self)
146    }
147}