| #![deny(unsafe_code)] |
| |
| //! Caching handle into the [ArcSwapAny]. |
| //! |
| //! The [Cache] keeps a copy of the internal [Arc] for faster access. |
| //! |
| //! [Arc]: std::sync::Arc |
| |
| use core::ops::Deref; |
| use core::sync::atomic::Ordering; |
| |
| use super::ref_cnt::RefCnt; |
| use super::strategy::Strategy; |
| use super::ArcSwapAny; |
| |
| /// Generalization of caches providing access to `T`. |
| /// |
| /// This abstracts over all kinds of caches that can provide a cheap access to values of type `T`. |
| /// This is useful in cases where some code doesn't care if the `T` is the whole structure or just |
| /// a part of it. |
| /// |
| /// See the example at [`Cache::map`]. |
| pub trait Access<T> { |
| /// Loads the value from cache. |
| /// |
| /// This revalidates the value in the cache, then provides the access to the cached value. |
| fn load(&mut self) -> &T; |
| } |
| |
| /// Caching handle for [`ArcSwapAny`][ArcSwapAny]. |
| /// |
| /// Instead of loading the [`Arc`][Arc] on every request from the shared storage, this keeps |
| /// another copy inside itself. Upon request it only cheaply revalidates it is up to |
| /// date. If it is, access is significantly faster. If it is stale, the [load_full] is done and the |
| /// cache value is replaced. Under a read-heavy loads, the measured speedup are 10-25 times, |
| /// depending on the architecture. |
| /// |
| /// There are, however, downsides: |
| /// |
| /// * The handle needs to be kept around by the caller (usually, one per thread). This is fine if |
| /// there's one global `ArcSwapAny`, but starts being tricky with eg. data structures build from |
| /// them. |
| /// * As it keeps a copy of the [Arc] inside the cache, the old value may be kept alive for longer |
| /// period of time ‒ it is replaced by the new value on [load][Cache::load]. You may not want to |
| /// use this if dropping the old value in timely manner is important (possibly because of |
| /// releasing large amount of RAM or because of closing file handles). |
| /// |
| /// # Examples |
| /// |
| /// ```rust |
| /// # fn do_something<V>(_v: V) { } |
| /// use std::sync::Arc; |
| /// use std::sync::atomic::{AtomicBool, Ordering}; |
| /// |
| /// use arc_swap::{ArcSwap, Cache}; |
| /// |
| /// let shared = Arc::new(ArcSwap::from_pointee(42)); |
| /// # let mut threads = Vec::new(); |
| /// let terminate = Arc::new(AtomicBool::new(false)); |
| /// // Start 10 worker threads... |
| /// for _ in 0..10 { |
| /// let mut cache = Cache::new(Arc::clone(&shared)); |
| /// let terminate = Arc::clone(&terminate); |
| /// # let thread = |
| /// std::thread::spawn(move || { |
| /// // Keep loading it like mad.. |
| /// while !terminate.load(Ordering::Relaxed) { |
| /// let value = cache.load(); |
| /// do_something(value); |
| /// } |
| /// }); |
| /// # threads.push(thread); |
| /// } |
| /// shared.store(Arc::new(12)); |
| /// # terminate.store(true, Ordering::Relaxed); |
| /// # for thread in threads { thread.join().unwrap() } |
| /// ``` |
| /// |
| /// Another one with using a thread local storage and explicit types: |
| /// |
| /// ```rust |
| /// # use std::sync::Arc; |
| /// # use std::ops::Deref; |
| /// # use std::cell::RefCell; |
| /// # |
| /// # use arc_swap::ArcSwap; |
| /// # use arc_swap::cache::Cache; |
| /// # use once_cell::sync::Lazy; |
| /// # |
| /// # #[derive(Debug, Default)] |
| /// # struct Config; |
| /// # |
| /// static CURRENT_CONFIG: Lazy<ArcSwap<Config>> = Lazy::new(|| ArcSwap::from_pointee(Config::default())); |
| /// |
| /// thread_local! { |
| /// static CACHE: RefCell<Cache<&'static ArcSwap<Config>, Arc<Config>>> = RefCell::new(Cache::from(CURRENT_CONFIG.deref())); |
| /// } |
| /// |
| /// CACHE.with(|c| { |
| /// // * RefCell needed, because load on cache is `&mut`. |
| /// // * You want to operate inside the `with` ‒ cloning the Arc is comparably expensive as |
| /// // ArcSwap::load itself and whatever you'd save by the cache would be lost on that. |
| /// println!("{:?}", c.borrow_mut().load()); |
| /// }); |
| /// ``` |
| /// |
| /// [Arc]: std::sync::Arc |
| /// [load_full]: ArcSwapAny::load_full |
| #[derive(Clone, Debug)] |
| pub struct Cache<A, T> { |
| arc_swap: A, |
| cached: T, |
| } |
| |
| impl<A, T, S> Cache<A, T> |
| where |
| A: Deref<Target = ArcSwapAny<T, S>>, |
| T: RefCnt, |
| S: Strategy<T>, |
| { |
| /// Creates a new caching handle. |
| /// |
| /// The parameter is something dereferencing into an [`ArcSwapAny`] (eg. either to [`ArcSwap`] |
| /// or [`ArcSwapOption`]). That can be [`ArcSwapAny`] itself, but that's not very useful. But |
| /// it also can be a reference to it or `Arc`, which makes it possible to share the |
| /// [`ArcSwapAny`] with multiple caches or access it in non-cached way too. |
| /// |
| /// [`ArcSwapOption`]: crate::ArcSwapOption |
| /// [`ArcSwap`]: crate::ArcSwap |
| pub fn new(arc_swap: A) -> Self { |
| let cached = arc_swap.load_full(); |
| Self { arc_swap, cached } |
| } |
| |
| /// Gives access to the (possibly shared) cached [`ArcSwapAny`]. |
| pub fn arc_swap(&self) -> &A::Target { |
| &self.arc_swap |
| } |
| |
| /// Loads the currently held value. |
| /// |
| /// This first checks if the cached value is up to date. This check is very cheap. |
| /// |
| /// If it is up to date, the cached value is simply returned without additional costs. If it is |
| /// outdated, a load is done on the underlying shared storage. The newly loaded value is then |
| /// stored in the cache and returned. |
| #[inline] |
| pub fn load(&mut self) -> &T { |
| self.revalidate(); |
| self.load_no_revalidate() |
| } |
| |
| #[inline] |
| fn load_no_revalidate(&self) -> &T { |
| &self.cached |
| } |
| |
| #[inline] |
| fn revalidate(&mut self) { |
| let cached_ptr = RefCnt::as_ptr(&self.cached); |
| // Node: Relaxed here is fine. We do not synchronize any data through this, we already have |
| // it synchronized in self.cache. We just want to check if it changed, if it did, the |
| // load_full will be responsible for any synchronization needed. |
| let shared_ptr = self.arc_swap.ptr.load(Ordering::Relaxed); |
| if cached_ptr != shared_ptr { |
| self.cached = self.arc_swap.load_full(); |
| } |
| } |
| |
| /// Turns this cache into a cache with a projection inside the cached value. |
| /// |
| /// You'd use this in case when some part of code needs access to fresh values of `U`, however |
| /// a bigger structure containing `U` is provided by this cache. The possibility of giving the |
| /// whole structure to the part of the code falls short in terms of reusability (the part of |
| /// the code could be used within multiple contexts, each with a bigger different structure |
| /// containing `U`) and code separation (the code shouldn't needs to know about the big |
| /// structure). |
| /// |
| /// # Warning |
| /// |
| /// As the provided `f` is called inside every [`load`][Access::load], this one should be |
| /// cheap. Most often it is expected to be just a closure taking reference of some inner field. |
| /// |
| /// For the same reasons, it should not have side effects and should never panic (these will |
| /// not break Rust's safety rules, but might produce behaviour you don't expect). |
| /// |
| /// # Examples |
| /// |
| /// ```rust |
| /// use arc_swap::ArcSwap; |
| /// use arc_swap::cache::{Access, Cache}; |
| /// |
| /// struct InnerCfg { |
| /// answer: usize, |
| /// } |
| /// |
| /// struct FullCfg { |
| /// inner: InnerCfg, |
| /// } |
| /// |
| /// fn use_inner<A: Access<InnerCfg>>(cache: &mut A) { |
| /// let value = cache.load(); |
| /// println!("The answer is: {}", value.answer); |
| /// } |
| /// |
| /// let full_cfg = ArcSwap::from_pointee(FullCfg { |
| /// inner: InnerCfg { |
| /// answer: 42, |
| /// } |
| /// }); |
| /// let cache = Cache::new(&full_cfg); |
| /// use_inner(&mut cache.map(|full| &full.inner)); |
| /// |
| /// let inner_cfg = ArcSwap::from_pointee(InnerCfg { answer: 24 }); |
| /// let mut inner_cache = Cache::new(&inner_cfg); |
| /// use_inner(&mut inner_cache); |
| /// ``` |
| pub fn map<F, U>(self, f: F) -> MapCache<A, T, F> |
| where |
| F: FnMut(&T) -> &U, |
| { |
| MapCache { |
| inner: self, |
| projection: f, |
| } |
| } |
| } |
| |
| impl<A, T, S> Access<T::Target> for Cache<A, T> |
| where |
| A: Deref<Target = ArcSwapAny<T, S>>, |
| T: Deref<Target = <T as RefCnt>::Base> + RefCnt, |
| S: Strategy<T>, |
| { |
| fn load(&mut self) -> &T::Target { |
| self.load().deref() |
| } |
| } |
| |
| impl<A, T, S> From<A> for Cache<A, T> |
| where |
| A: Deref<Target = ArcSwapAny<T, S>>, |
| T: RefCnt, |
| S: Strategy<T>, |
| { |
| fn from(arc_swap: A) -> Self { |
| Self::new(arc_swap) |
| } |
| } |
| |
| /// An implementation of a cache with a projection into the accessed value. |
| /// |
| /// This is the implementation structure for [`Cache::map`]. It can't be created directly and it |
| /// should be used through the [`Access`] trait. |
| #[derive(Clone, Debug)] |
| pub struct MapCache<A, T, F> { |
| inner: Cache<A, T>, |
| projection: F, |
| } |
| |
| impl<A, T, S, F, U> Access<U> for MapCache<A, T, F> |
| where |
| A: Deref<Target = ArcSwapAny<T, S>>, |
| T: RefCnt, |
| S: Strategy<T>, |
| F: FnMut(&T) -> &U, |
| { |
| fn load(&mut self) -> &U { |
| (self.projection)(self.inner.load()) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use alloc::sync::Arc; |
| |
| use super::*; |
| use crate::{ArcSwap, ArcSwapOption}; |
| |
| #[test] |
| fn cached_value() { |
| let a = ArcSwap::from_pointee(42); |
| let mut c1 = Cache::new(&a); |
| let mut c2 = Cache::new(&a); |
| |
| assert_eq!(42, **c1.load()); |
| assert_eq!(42, **c2.load()); |
| |
| a.store(Arc::new(43)); |
| assert_eq!(42, **c1.load_no_revalidate()); |
| assert_eq!(43, **c1.load()); |
| } |
| |
| #[test] |
| fn cached_through_arc() { |
| let a = Arc::new(ArcSwap::from_pointee(42)); |
| let mut c = Cache::new(Arc::clone(&a)); |
| assert_eq!(42, **c.load()); |
| a.store(Arc::new(0)); |
| drop(a); // A is just one handle, the ArcSwap is kept alive by the cache. |
| } |
| |
| #[test] |
| fn cache_option() { |
| let a = ArcSwapOption::from_pointee(42); |
| let mut c = Cache::new(&a); |
| |
| assert_eq!(42, **c.load().as_ref().unwrap()); |
| a.store(None); |
| assert!(c.load().is_none()); |
| } |
| |
| struct Inner { |
| answer: usize, |
| } |
| |
| struct Outer { |
| inner: Inner, |
| } |
| |
| #[test] |
| fn map_cache() { |
| let a = ArcSwap::from_pointee(Outer { |
| inner: Inner { answer: 42 }, |
| }); |
| |
| let mut cache = Cache::new(&a); |
| let mut inner = cache.clone().map(|outer| &outer.inner); |
| let mut answer = cache.clone().map(|outer| &outer.inner.answer); |
| |
| assert_eq!(42, cache.load().inner.answer); |
| assert_eq!(42, inner.load().answer); |
| assert_eq!(42, *answer.load()); |
| |
| a.store(Arc::new(Outer { |
| inner: Inner { answer: 24 }, |
| })); |
| |
| assert_eq!(24, cache.load().inner.answer); |
| assert_eq!(24, inner.load().answer); |
| assert_eq!(24, *answer.load()); |
| } |
| } |