kumo_server_common/
config_handle.rs

1use arc_swap::{ArcSwap, Guard};
2use std::fmt::Debug;
3use std::sync::atomic::{AtomicUsize, Ordering};
4use std::sync::Arc;
5
6/// ConfigHandle allows sharing a configuration value of some type T
7/// without requiring full Mutex around every read operation.
8///
9/// The update method update the upstream value, bumping
10/// the generation counter in the process.
11///
12/// The borrow method will return a Guard referencing the value
13/// without requiring a Mutex lock operation.
14#[derive(Clone)]
15pub struct ConfigHandle<T: Clone + Send> {
16    inner: Arc<Inner<T>>,
17}
18
19struct Inner<T: Clone + Send> {
20    value: ArcSwap<T>,
21    generation: AtomicUsize,
22}
23
24impl<T: Clone + Send + Debug> Debug for ConfigHandle<T> {
25    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        fmt.debug_struct("ConfigHandle")
27            .field("value", &self.inner.value)
28            .field("generation", &self.inner.generation)
29            .finish()
30    }
31}
32
33impl<T: Clone + Send> ConfigHandle<T> {
34    pub fn new(value: T) -> Self {
35        Self {
36            inner: Arc::new(Inner {
37                value: ArcSwap::from_pointee(value),
38                generation: AtomicUsize::new(0),
39            }),
40        }
41    }
42
43    pub fn generation(&self) -> usize {
44        self.inner.generation.load(Ordering::SeqCst)
45    }
46
47    /// Updates the upstream, shared value.
48    /// Other config handles will notice the change when borrow() is subsequently called.
49    pub fn update(&self, new_value: T) -> usize {
50        self.inner.value.swap(Arc::new(new_value));
51
52        self.inner.generation.fetch_add(1, Ordering::SeqCst) + 1
53    }
54
55    /// Borrows the local copy of the config for read.
56    /// The local copy will be updated from the upstream if necessary.
57    pub fn borrow(&self) -> Guard<Arc<T>> {
58        self.inner.value.load()
59    }
60}