Skip to main content

fast_telemetry/metric/
gauge.rs

1//! Atomic gauge for point-in-time values.
2
3use crossbeam_utils::CachePadded;
4use std::sync::atomic::{AtomicI64, Ordering};
5
6/// A cache-padded atomic gauge for point-in-time measurements.
7///
8/// Gauges represent a current value that is periodically sampled and set,
9/// such as memory usage, queue depth, or progress percentage.
10///
11/// # Why Only `set()` and `get()`?
12///
13/// This type intentionally omits `add()`/`inc()`/`dec()` methods. If you need
14/// to increment or decrement a value from multiple threads (e.g., tracking
15/// active connections), use [`Counter`] instead - it's thread-sharded for
16/// contention-free concurrent increments.
17///
18/// Providing increment methods here would create a footgun: the API would look
19/// convenient for counting, but the single-atomic implementation would cause
20/// cache-line contention under concurrent writes. By limiting the API to
21/// `set()`/`get()`, we make the intended usage pattern clear: periodic
22/// point-in-time snapshots from a single writer.
23///
24/// # Why Not Thread-Sharded?
25///
26/// Thread-sharding works for `Counter` because addition is commutative — you
27/// can sum the shards to get the total. This includes subtraction (adding
28/// negative values): the shard sums still produce the correct aggregate.
29/// `set()` is not commutative; there's no meaningful way to aggregate "last
30/// value written" across shards.
31///
32/// The cache padding prevents false sharing if this gauge is stored adjacent
33/// to frequently-accessed data.
34///
35/// [`Counter`]: crate::Counter
36pub struct Gauge {
37    value: CachePadded<AtomicI64>,
38}
39
40impl Gauge {
41    /// Create a new gauge initialized to zero.
42    pub fn new() -> Self {
43        Self {
44            value: CachePadded::new(AtomicI64::new(0)),
45        }
46    }
47
48    /// Create a new gauge with an initial value.
49    pub fn with_value(initial: i64) -> Self {
50        Self {
51            value: CachePadded::new(AtomicI64::new(initial)),
52        }
53    }
54
55    /// Set the gauge to a value.
56    #[inline]
57    pub fn set(&self, value: i64) {
58        self.value.store(value, Ordering::Relaxed);
59    }
60
61    /// Get the current value.
62    #[inline]
63    pub fn get(&self) -> i64 {
64        self.value.load(Ordering::Relaxed)
65    }
66}
67
68impl Default for Gauge {
69    fn default() -> Self {
70        Self::new()
71    }
72}