1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use arc_swap::{ArcSwap, Guard};
use std::fmt::Debug;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

/// ConfigHandle allows sharing a configuration value of some type T
/// without requiring full Mutex around every read operation.
///
/// The update method update the upstream value, bumping
/// the generation counter in the process.
///
/// The borrow method will return a Guard referencing the value
/// without requiring a Mutex lock operation.
#[derive(Clone)]
pub struct ConfigHandle<T: Clone + Send> {
    inner: Arc<Inner<T>>,
}

struct Inner<T: Clone + Send> {
    value: ArcSwap<T>,
    generation: AtomicUsize,
}

impl<T: Clone + Send + Debug> Debug for ConfigHandle<T> {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        fmt.debug_struct("ConfigHandle")
            .field("value", &self.inner.value)
            .field("generation", &self.inner.generation)
            .finish()
    }
}

impl<T: Clone + Send> ConfigHandle<T> {
    pub fn new(value: T) -> Self {
        Self {
            inner: Arc::new(Inner {
                value: ArcSwap::from_pointee(value),
                generation: AtomicUsize::new(0),
            }),
        }
    }

    pub fn generation(&self) -> usize {
        self.inner.generation.load(Ordering::SeqCst)
    }

    /// Updates the upstream, shared value.
    /// Other config handles will notice the change when borrow() is subsequently called.
    pub fn update(&self, new_value: T) -> usize {
        self.inner.value.swap(Arc::new(new_value));
        let generation = self.inner.generation.fetch_add(1, Ordering::SeqCst) + 1;
        generation
    }

    /// Borrows the local copy of the config for read.
    /// The local copy will be updated from the upstream if necessary.
    pub fn borrow(&self) -> Guard<Arc<T>> {
        self.inner.value.load()
    }
}