Coverage Report

Created: 2026-03-31 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/arc-swap-1.9.0/src/cache.rs
Line
Count
Source
1
#![deny(unsafe_code)]
2
3
//! Caching handle into the [ArcSwapAny].
4
//!
5
//! The [Cache] keeps a copy of the internal [Arc] for faster access.
6
//!
7
//! [Arc]: std::sync::Arc
8
9
use core::ops::Deref;
10
use core::sync::atomic::Ordering;
11
12
use super::ref_cnt::RefCnt;
13
use super::strategy::Strategy;
14
use super::ArcSwapAny;
15
16
/// Generalization of caches providing access to `T`.
17
///
18
/// This abstracts over all kinds of caches that can provide a cheap access to values of type `T`.
19
/// This is useful in cases where some code doesn't care if the `T` is the whole structure or just
20
/// a part of it.
21
///
22
/// See the example at [`Cache::map`].
23
pub trait Access<T> {
24
    /// Loads the value from cache.
25
    ///
26
    /// This revalidates the value in the cache, then provides the access to the cached value.
27
    fn load(&mut self) -> &T;
28
}
29
30
/// Caching handle for [`ArcSwapAny`].
31
///
32
/// Instead of loading the [`Arc`][Arc] on every request from the shared storage, this keeps
33
/// another copy inside itself. Upon request it only cheaply revalidates it is up to
34
/// date. If it is, access is significantly faster. If it is stale, the [load_full] is done and the
35
/// cache value is replaced. Under a read-heavy loads, the measured speedup are 10-25 times,
36
/// depending on the architecture.
37
///
38
/// There are, however, downsides:
39
///
40
/// * The handle needs to be kept around by the caller (usually, one per thread). This is fine if
41
///   there's one global `ArcSwapAny`, but starts being tricky with eg. data structures build from
42
///   them.
43
/// * As it keeps a copy of the [Arc] inside the cache, the old value may be kept alive for longer
44
///   period of time ‒ it is replaced by the new value on [load][Cache::load]. You may not want to
45
///   use this if dropping the old value in timely manner is important (possibly because of
46
///   releasing large amount of RAM or because of closing file handles).
47
///
48
/// # Examples
49
///
50
/// ```rust
51
/// # fn do_something<V>(_v: V) { }
52
/// use std::sync::Arc;
53
/// use std::sync::atomic::{AtomicBool, Ordering};
54
///
55
/// use arc_swap::{ArcSwap, Cache};
56
///
57
/// let shared = Arc::new(ArcSwap::from_pointee(42));
58
/// # let mut threads = Vec::new();
59
/// let terminate = Arc::new(AtomicBool::new(false));
60
/// // Start 10 worker threads...
61
/// for _ in 0..10 {
62
///     let mut cache = Cache::new(Arc::clone(&shared));
63
///     let terminate = Arc::clone(&terminate);
64
///     # let thread =
65
///     std::thread::spawn(move || {
66
///         // Keep loading it like mad..
67
///         while !terminate.load(Ordering::Relaxed) {
68
///             let value = cache.load();
69
///             do_something(value);
70
///         }
71
///     });
72
///     # threads.push(thread);
73
/// }
74
/// shared.store(Arc::new(12));
75
/// # terminate.store(true, Ordering::Relaxed);
76
/// # for thread in threads { thread.join().unwrap() }
77
/// ```
78
///
79
/// Another one with using a thread local storage and explicit types:
80
///
81
/// ```rust
82
/// # use std::sync::Arc;
83
/// # use std::ops::Deref;
84
/// # use std::cell::RefCell;
85
/// #
86
/// # use arc_swap::ArcSwap;
87
/// # use arc_swap::cache::Cache;
88
/// # use once_cell::sync::Lazy;
89
/// #
90
/// # #[derive(Debug, Default)]
91
/// # struct Config;
92
/// #
93
/// static CURRENT_CONFIG: Lazy<ArcSwap<Config>> = Lazy::new(|| ArcSwap::from_pointee(Config::default()));
94
///
95
/// thread_local! {
96
///     static CACHE: RefCell<Cache<&'static ArcSwap<Config>, Arc<Config>>> = RefCell::new(Cache::from(CURRENT_CONFIG.deref()));
97
/// }
98
///
99
/// CACHE.with(|c| {
100
///     // * RefCell needed, because load on cache is `&mut`.
101
///     // * You want to operate inside the `with` ‒ cloning the Arc is comparably expensive as
102
///     //   ArcSwap::load itself and whatever you'd save by the cache would be lost on that.
103
///     println!("{:?}", c.borrow_mut().load());
104
/// });
105
/// ```
106
///
107
/// [Arc]: std::sync::Arc
108
/// [load_full]: ArcSwapAny::load_full
109
#[derive(Clone, Debug)]
110
pub struct Cache<A, T> {
111
    arc_swap: A,
112
    cached: T,
113
}
114
115
impl<A, T, S> Cache<A, T>
116
where
117
    A: Deref<Target = ArcSwapAny<T, S>>,
118
    T: RefCnt,
119
    S: Strategy<T>,
120
{
121
    /// Creates a new caching handle.
122
    ///
123
    /// The parameter is something dereferencing into an [`ArcSwapAny`] (eg. either to [`ArcSwap`]
124
    /// or [`ArcSwapOption`]). That can be [`ArcSwapAny`] itself, but that's not very useful. But
125
    /// it also can be a reference to it or `Arc`, which makes it possible to share the
126
    /// [`ArcSwapAny`] with multiple caches or access it in non-cached way too.
127
    ///
128
    /// [`ArcSwapOption`]: crate::ArcSwapOption
129
    /// [`ArcSwap`]: crate::ArcSwap
130
0
    pub fn new(arc_swap: A) -> Self {
131
0
        let cached = arc_swap.load_full();
132
0
        Self { arc_swap, cached }
133
0
    }
134
135
    /// Gives access to the (possibly shared) cached [`ArcSwapAny`].
136
0
    pub fn arc_swap(&self) -> &A::Target {
137
0
        &self.arc_swap
138
0
    }
139
140
    /// Loads the currently held value.
141
    ///
142
    /// This first checks if the cached value is up to date. This check is very cheap.
143
    ///
144
    /// If it is up to date, the cached value is simply returned without additional costs. If it is
145
    /// outdated, a load is done on the underlying shared storage. The newly loaded value is then
146
    /// stored in the cache and returned.
147
    #[inline]
148
0
    pub fn load(&mut self) -> &T {
149
0
        self.revalidate();
150
0
        self.load_no_revalidate()
151
0
    }
152
153
    #[inline]
154
0
    fn load_no_revalidate(&self) -> &T {
155
0
        &self.cached
156
0
    }
157
158
    #[inline]
159
0
    fn revalidate(&mut self) {
160
0
        let cached_ptr = RefCnt::as_ptr(&self.cached);
161
        // Node: Relaxed here is fine. We do not synchronize any data through this, we already have
162
        // it synchronized in self.cache. We just want to check if it changed, if it did, the
163
        // load_full will be responsible for any synchronization needed.
164
0
        let shared_ptr = self.arc_swap.ptr.load(Ordering::Relaxed);
165
0
        if cached_ptr != shared_ptr {
166
0
            self.cached = self.arc_swap.load_full();
167
0
        }
168
0
    }
169
170
    /// Turns this cache into a cache with a projection inside the cached value.
171
    ///
172
    /// You'd use this in case when some part of code needs access to fresh values of `U`, however
173
    /// a bigger structure containing `U` is provided by this cache. The possibility of giving the
174
    /// whole structure to the part of the code falls short in terms of reusability (the part of
175
    /// the code could be used within multiple contexts, each with a bigger different structure
176
    /// containing `U`) and code separation (the code shouldn't needs to know about the big
177
    /// structure).
178
    ///
179
    /// # Warning
180
    ///
181
    /// As the provided `f` is called inside every [`load`][Access::load], this one should be
182
    /// cheap. Most often it is expected to be just a closure taking reference of some inner field.
183
    ///
184
    /// For the same reasons, it should not have side effects and should never panic (these will
185
    /// not break Rust's safety rules, but might produce behaviour you don't expect).
186
    ///
187
    /// # Examples
188
    ///
189
    /// ```rust
190
    /// use arc_swap::ArcSwap;
191
    /// use arc_swap::cache::{Access, Cache};
192
    ///
193
    /// struct InnerCfg {
194
    ///     answer: usize,
195
    /// }
196
    ///
197
    /// struct FullCfg {
198
    ///     inner: InnerCfg,
199
    /// }
200
    ///
201
    /// fn use_inner<A: Access<InnerCfg>>(cache: &mut A) {
202
    ///     let value = cache.load();
203
    ///     println!("The answer is: {}", value.answer);
204
    /// }
205
    ///
206
    /// let full_cfg = ArcSwap::from_pointee(FullCfg {
207
    ///     inner: InnerCfg {
208
    ///         answer: 42,
209
    ///     }
210
    /// });
211
    /// let cache = Cache::new(&full_cfg);
212
    /// use_inner(&mut cache.map(|full| &full.inner));
213
    ///
214
    /// let inner_cfg = ArcSwap::from_pointee(InnerCfg { answer: 24 });
215
    /// let mut inner_cache = Cache::new(&inner_cfg);
216
    /// use_inner(&mut inner_cache);
217
    /// ```
218
0
    pub fn map<F, U>(self, f: F) -> MapCache<A, T, F>
219
0
    where
220
0
        F: FnMut(&T) -> &U,
221
    {
222
0
        MapCache {
223
0
            inner: self,
224
0
            projection: f,
225
0
        }
226
0
    }
227
}
228
229
impl<A, T, S> Access<T::Target> for Cache<A, T>
230
where
231
    A: Deref<Target = ArcSwapAny<T, S>>,
232
    T: Deref<Target = <T as RefCnt>::Base> + RefCnt,
233
    S: Strategy<T>,
234
{
235
0
    fn load(&mut self) -> &T::Target {
236
0
        self.load().deref()
237
0
    }
238
}
239
240
impl<A, T, S> From<A> for Cache<A, T>
241
where
242
    A: Deref<Target = ArcSwapAny<T, S>>,
243
    T: RefCnt,
244
    S: Strategy<T>,
245
{
246
0
    fn from(arc_swap: A) -> Self {
247
0
        Self::new(arc_swap)
248
0
    }
249
}
250
251
/// An implementation of a cache with a projection into the accessed value.
252
///
253
/// This is the implementation structure for [`Cache::map`]. It can't be created directly and it
254
/// should be used through the [`Access`] trait.
255
#[derive(Clone, Debug)]
256
pub struct MapCache<A, T, F> {
257
    inner: Cache<A, T>,
258
    projection: F,
259
}
260
261
impl<A, T, S, F, U> Access<U> for MapCache<A, T, F>
262
where
263
    A: Deref<Target = ArcSwapAny<T, S>>,
264
    T: RefCnt,
265
    S: Strategy<T>,
266
    F: FnMut(&T) -> &U,
267
{
268
0
    fn load(&mut self) -> &U {
269
0
        (self.projection)(self.inner.load())
270
0
    }
271
}
272
273
#[cfg(test)]
274
mod tests {
275
    use alloc::sync::Arc;
276
277
    use super::*;
278
    use crate::{ArcSwap, ArcSwapOption};
279
280
    #[test]
281
    fn cached_value() {
282
        let a = ArcSwap::from_pointee(42);
283
        let mut c1 = Cache::new(&a);
284
        let mut c2 = Cache::new(&a);
285
286
        assert_eq!(42, **c1.load());
287
        assert_eq!(42, **c2.load());
288
289
        a.store(Arc::new(43));
290
        assert_eq!(42, **c1.load_no_revalidate());
291
        assert_eq!(43, **c1.load());
292
    }
293
294
    #[test]
295
    fn cached_through_arc() {
296
        let a = Arc::new(ArcSwap::from_pointee(42));
297
        let mut c = Cache::new(Arc::clone(&a));
298
        assert_eq!(42, **c.load());
299
        a.store(Arc::new(0));
300
        drop(a); // A is just one handle, the ArcSwap is kept alive by the cache.
301
    }
302
303
    #[test]
304
    fn cache_option() {
305
        let a = ArcSwapOption::from_pointee(42);
306
        let mut c = Cache::new(&a);
307
308
        assert_eq!(42, **c.load().as_ref().unwrap());
309
        a.store(None);
310
        assert!(c.load().is_none());
311
    }
312
313
    struct Inner {
314
        answer: usize,
315
    }
316
317
    struct Outer {
318
        inner: Inner,
319
    }
320
321
    #[test]
322
    fn map_cache() {
323
        let a = ArcSwap::from_pointee(Outer {
324
            inner: Inner { answer: 42 },
325
        });
326
327
        let mut cache = Cache::new(&a);
328
        let mut inner = cache.clone().map(|outer| &outer.inner);
329
        let mut answer = cache.clone().map(|outer| &outer.inner.answer);
330
331
        assert_eq!(42, cache.load().inner.answer);
332
        assert_eq!(42, inner.load().answer);
333
        assert_eq!(42, *answer.load());
334
335
        a.store(Arc::new(Outer {
336
            inner: Inner { answer: 24 },
337
        }));
338
339
        assert_eq!(24, cache.load().inner.answer);
340
        assert_eq!(24, inner.load().answer);
341
        assert_eq!(24, *answer.load());
342
    }
343
}