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}