Skip to main content

cfg_rs/
value_ref.rs

1use crate::macros::cfg_log;
2use crate::*;
3
4/// [`RefValue`] means reference of value or refreshable value,
5/// it holds a value which can be updated when [`Configuration`] is refreshed.
6///
7/// It implements [`FromConfig`], user can use it in auto deriving config objects.
8///
9/// But it is not supporting **recursively** usage, following example will cause runtime error:
10/// ```rust,ignore
11/// #[derive(FromConfig)]
12/// struct A {
13///   ref_b: RefValue<B>,
14/// }
15/// #[derive(FromConfig)]
16/// struct B {
17///   ref_c: RefValue<u8>,
18/// }
19/// ```
20///
21/// `RefValue` can be read from multiple threads, but registration/refresh is expected
22/// to happen on a single thread (see `Refresher`).
23#[allow(missing_debug_implementations)]
24pub struct RefValue<T>(Arc<Mutex<T>>, String);
25
26impl<T> Clone for RefValue<T> {
27    fn clone(&self) -> Self {
28        RefValue(self.0.clone(), self.1.clone())
29    }
30}
31
32impl<T> RefValue<T> {
33    fn new(k: String, v: T) -> Self {
34        Self(Arc::new(Mutex::new(v)), k)
35    }
36
37    fn set(&self, v: T) -> Result<(), ConfigError> {
38        *self.0.lock_c()? = v;
39        Ok(())
40    }
41
42    /// Use referenced value, be careful with lock.
43    pub fn with<F: FnOnce(&T) -> R, R>(&self, f: F) -> Result<R, ConfigError> {
44        let g = self.0.lock_c()?;
45        Ok((f)(&*g))
46    }
47}
48impl<T: Clone> RefValue<T> {
49    /// Get cloned value.
50    pub fn get(&self) -> Result<T, ConfigError> {
51        self.with(|v| v.clone())
52    }
53}
54
55impl<T: FromConfig + Send + 'static> FromConfig for RefValue<T> {
56    fn from_config(
57        context: &mut ConfigContext<'_>,
58        value: Option<ConfigValue<'_>>,
59    ) -> Result<Self, ConfigError> {
60        if context.ref_value_flag {
61            return Err(ConfigError::RefValueRecursiveError);
62        }
63        context.ref_value_flag = true;
64        let v = do_from_config(context, value);
65        context.ref_value_flag = false;
66        if v.is_ok() {
67            cfg_log!(
68                log::Level::Debug,
69                "RefValue {} registered!",
70                context.current_key_str()
71            );
72        }
73        v
74    }
75}
76
77#[inline]
78fn do_from_config<T: FromConfig + Send + 'static>(
79    context: &mut ConfigContext<'_>,
80    value: Option<ConfigValue<'_>>,
81) -> Result<RefValue<T>, ConfigError> {
82    let v = RefValue::new(context.current_key(), T::from_config(context, value)?);
83    context.as_refresher().push(v.clone())?;
84    Ok(v)
85}
86
87trait Ref: Send {
88    fn refresh(&self, config: &Configuration) -> Result<(), ConfigError>;
89}
90
91impl<T: FromConfig + Send> Ref for RefValue<T> {
92    fn refresh(&self, config: &Configuration) -> Result<(), ConfigError> {
93        cfg_log!(log::Level::Debug, "RefValue {} refreshing...", &self.1);
94        self.set(config.get(&self.1)?)
95    }
96}
97
98pub(crate) struct Refresher {
99    max: usize,
100    refs: Mutex<Vec<Box<dyn Ref + Send + 'static>>>,
101}
102
103impl Refresher {
104    pub(crate) fn new() -> Self {
105        Self {
106            max: 1024,
107            refs: Mutex::new(vec![]),
108        }
109    }
110
111    fn push(&self, r: impl Ref + 'static) -> Result<(), ConfigError> {
112        // Use try_lock to avoid re-entrant deadlocks during refresh.
113        // Lock contention is treated as recursive usage; Refresher should be single-threaded.
114        let g = self.refs.try_lock_c()?;
115        if let Some(mut g) = g {
116            if g.len() >= self.max {
117                return Err(ConfigError::TooManyInstances(self.max));
118            }
119            g.push(Box::new(r));
120            Ok(())
121        } else {
122            Err(ConfigError::RefValueRecursiveError)
123        }
124    }
125
126    pub(crate) fn refresh(&self, c: &Configuration) -> Result<(), ConfigError> {
127        let g = self.refs.lock_c()?;
128        let it = g.iter();
129        for i in it {
130            i.refresh(c)?;
131        }
132        Ok(())
133    }
134}
135
136#[cfg_attr(coverage_nightly, coverage(off))]
137#[cfg(test)]
138mod test {
139    use std::sync::Arc;
140
141    use super::{Ref, Refresher};
142    use crate::err::ConfigLock;
143    use crate::{
144        Mutex,
145        source::{ConfigSource, ConfigSourceBuilder, memory::HashSource},
146        *,
147    };
148
149    #[derive(FromConfig)]
150    #[config(crate = "crate")]
151    struct A {
152        _ref_b: RefValue<B>,
153    }
154    #[derive(FromConfig)]
155    #[config(crate = "crate")]
156    struct B {
157        _ref_c: RefValue<u8>,
158    }
159
160    macro_rules! should_err {
161        ($v:ident) => {
162            assert_eq!(true, $v.is_err());
163            match $v.err().unwrap() {
164                ConfigError::RefValueRecursiveError => {}
165                e => {
166                    println!("{:?}", e);
167                    assert_eq!(true, false)
168                }
169            }
170        };
171    }
172
173    #[test]
174    fn recursive_test() {
175        let config = Configuration::new();
176        let v = config.get::<A>("hello");
177        should_err!(v);
178        let v = config.get::<RefValue<B>>("hello");
179        should_err!(v);
180        let v = config.get::<RefValue<RefValue<u8>>>("hello");
181        should_err!(v);
182    }
183
184    struct R(Arc<Mutex<(u64, bool)>>);
185
186    impl ConfigSource for R {
187        fn name(&self) -> &str {
188            "r"
189        }
190
191        fn load(&self, builder: &mut ConfigSourceBuilder<'_>) -> Result<(), ConfigError> {
192            builder.set("hello", self.0.lock_c()?.0);
193            Ok(())
194        }
195
196        fn allow_refresh(&self) -> bool {
197            true
198        }
199
200        fn refreshable(&self) -> Result<bool, ConfigError> {
201            let mut g = self.0.lock_c()?;
202            let flag = g.1;
203            g.1 = false;
204            Ok(flag)
205        }
206    }
207
208    impl R {
209        fn set(&self, v: u64) {
210            *self.0.lock_c().unwrap() = (v, true);
211        }
212
213        fn get(&self) -> u64 {
214            self.0.lock_c().unwrap().0
215        }
216    }
217
218    macro_rules! should_eq {
219        ($config:ident: $r:ident. $v:ident = $i:ident) => {
220            $r.set($i);
221            assert_eq!(true, $config.refresh_ref().unwrap());
222            assert_eq!(false, $config.refresh_ref().unwrap());
223            assert_eq!($i, $v.get().unwrap());
224            assert_eq!(0, $config.get::<u64>("hello").unwrap());
225        };
226    }
227
228    #[test]
229    fn refresh_test() {
230        let r = R(Arc::new(Mutex::new((0, true))));
231        assert_eq!("r", r.name());
232        let config = Configuration::new()
233            .register_source(R(r.0.clone()))
234            .unwrap();
235        let v = config.get::<RefValue<u64>>("hello").unwrap();
236
237        for i in 0..1000 {
238            should_eq!(config: r.v = i);
239        }
240    }
241
242    macro_rules! should_eq_mut {
243        ($config:ident: $r:ident. $v:ident = $i:ident) => {
244            $r.set($i);
245            assert_eq!(true, $config.refresh().unwrap());
246            assert_eq!(false, $config.refresh().unwrap());
247            assert_eq!($i, $v.get().unwrap());
248            assert_eq!($i, $config.get::<u64>("hello").unwrap());
249        };
250    }
251    #[test]
252    fn refresh_mut_test() {
253        let r = R(Arc::new(Mutex::new((0, true))));
254        assert_eq!("r", r.name());
255        let mut config = Configuration::new()
256            .register_source(R(r.0.clone()))
257            .unwrap();
258        let v = config.get::<RefValue<u64>>("hello").unwrap();
259
260        for i in 0..1000 {
261            should_eq_mut!(config: r.v = i);
262        }
263    }
264
265    struct DummyRef;
266
267    impl Ref for DummyRef {
268        fn refresh(&self, _: &Configuration) -> Result<(), ConfigError> {
269            Ok(())
270        }
271    }
272
273    #[test]
274    fn refresher_try_lock_conflict_returns_error() {
275        let refresher = Refresher::new();
276        let _guard = refresher.refs.lock_c().unwrap();
277
278        let err = refresher.push(DummyRef).unwrap_err();
279        match err {
280            ConfigError::RefValueRecursiveError => {}
281            _ => panic!("unexpected error: {:?}", err),
282        }
283    }
284
285    macro_rules! should_eq_2 {
286        ($config:ident: $r:ident.$s:ident. $v:ident = $i:ident) => {
287            $s.set($i);
288            assert_eq!(true, $config.refresh().unwrap());
289            assert_eq!(false, $config.refresh().unwrap());
290            assert_eq!($r.get(), $v.get().unwrap());
291            $r.set($i);
292            assert_eq!($i, $r.get());
293            assert_ne!($r.get(), $v.get().unwrap());
294            assert_eq!(true, $config.refresh().unwrap());
295            assert_eq!(false, $config.refresh().unwrap());
296            assert_eq!($i, $v.get().unwrap());
297            assert_eq!($i, $config.get::<u64>("hello").unwrap());
298        };
299    }
300
301    #[test]
302    fn multiple_source_refresh_test() {
303        let a = HashSource::new("name");
304        let r = R(Arc::new(Mutex::new((0, true))));
305        let s = R(Arc::new(Mutex::new((0, true))));
306        assert_eq!("r", r.name());
307        let mut config = Configuration::new()
308            .register_source(a)
309            .unwrap()
310            .register_source(R(r.0.clone()))
311            .unwrap()
312            .register_source(R(s.0.clone()))
313            .unwrap();
314        let v = config.get::<RefValue<u64>>("hello").unwrap();
315
316        for i in 1..1000 {
317            should_eq_2!(config: r.s.v = i);
318        }
319    }
320}