tor_config/
mut_cfg.rs

1//! Helper type for making configurations mutable.
2
3use std::sync::{Arc, RwLock};
4
5/// A mutable configuration object.
6///
7/// Internally, this is just a `RwLock<Arc<T>>`; this type just defines some
8/// convenience wrappers for it.
9#[derive(Debug, Default)]
10pub struct MutCfg<T> {
11    /// The interior configuration object.
12    cfg: RwLock<Arc<T>>,
13}
14
15impl<T> MutCfg<T> {
16    /// Return a new MutCfg with the provided value.
17    pub fn new(config: T) -> Self {
18        Self {
19            cfg: RwLock::new(Arc::new(config)),
20        }
21    }
22
23    /// Return the current configuration
24    pub fn get(&self) -> Arc<T> {
25        Arc::clone(&self.cfg.read().expect("poisoned lock"))
26    }
27
28    /// If this configuration object is still the same pointer as `old_config`,
29    /// replace it with `new_config`.
30    ///
31    /// Returns `true` if it was in fact replaced.
32    pub fn check_and_replace(&self, old_config: &Arc<T>, new_config: T) -> bool {
33        let mut cfg = self.cfg.write().expect("poisoned lock");
34        if Arc::ptr_eq(&cfg, old_config) {
35            *cfg = Arc::new(new_config);
36            true
37        } else {
38            false
39        }
40    }
41
42    /// Replace this configuration with `new_config`.
43    pub fn replace(&self, new_config: T) {
44        *self.cfg.write().expect("poisoned lock") = Arc::new(new_config);
45    }
46
47    /// Replace the current configuration with the results of evaluating `func` on it.
48    pub fn map_and_replace<F>(&self, func: F)
49    where
50        F: FnOnce(&Arc<T>) -> T,
51    {
52        let mut cfg = self.cfg.write().expect("poisoned lock");
53        let new_cfg = func(&cfg);
54        *cfg = Arc::new(new_cfg);
55    }
56}
57
58impl<T> From<T> for MutCfg<T> {
59    fn from(config: T) -> MutCfg<T> {
60        MutCfg::new(config)
61    }
62}
63
64#[cfg(test)]
65mod test {
66    // @@ begin test lint list maintained by maint/add_warning @@
67    #![allow(clippy::bool_assert_comparison)]
68    #![allow(clippy::clone_on_copy)]
69    #![allow(clippy::dbg_macro)]
70    #![allow(clippy::mixed_attributes_style)]
71    #![allow(clippy::print_stderr)]
72    #![allow(clippy::print_stdout)]
73    #![allow(clippy::single_char_pattern)]
74    #![allow(clippy::unwrap_used)]
75    #![allow(clippy::unchecked_duration_subtraction)]
76    #![allow(clippy::useless_vec)]
77    #![allow(clippy::needless_pass_by_value)]
78    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
79    use super::*;
80
81    #[test]
82    fn basic_constructors() {
83        let m = MutCfg::new(7_u32);
84        assert_eq!(*m.get(), 7);
85        let m: MutCfg<u32> = MutCfg::default();
86        assert_eq!(*m.get(), 0);
87        let m: MutCfg<u32> = 100.into();
88        assert_eq!(*m.get(), 100);
89    }
90
91    #[test]
92    fn mutate_with_existing_ref() {
93        let m = MutCfg::new(100_u32);
94        let old_ref = m.get();
95        m.replace(101);
96        assert_eq!(*old_ref, 100);
97        assert_eq!(*m.get(), 101);
98    }
99
100    #[test]
101    fn check_and_replace() {
102        let m = MutCfg::new(100_u32);
103        let different_100 = Arc::new(100_u32);
104        // won't replace, since it is a different arc.
105        assert!(!m.check_and_replace(&different_100, 200));
106        let old_100 = m.get();
107        assert_eq!(*old_100, 100);
108        assert!(m.check_and_replace(&old_100, 200));
109        assert_eq!(*m.get(), 200);
110    }
111
112    #[test]
113    fn map_and_replace() {
114        let m = MutCfg::new(100_u32);
115        let m_old = m.get();
116        m.map_and_replace(|old_val| **old_val * 20);
117        assert_eq!(*m.get(), 2000);
118        assert_eq!(*m_old, 100);
119    }
120}