#![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 std::ops::Deref; use std::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 { /// 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) { } /// 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> = Lazy::new(|| ArcSwap::from_pointee(Config::default())); /// /// thread_local! { /// static CACHE: RefCell, Arc>> = 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 { arc_swap: A, cached: T, } impl Cache where A: Deref>, T: RefCnt, S: Strategy, { /// 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>(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(self, f: F) -> MapCache where F: FnMut(&T) -> &U, { MapCache { inner: self, projection: f, } } } impl Access for Cache where A: Deref>, T: Deref::Base> + RefCnt, S: Strategy, { fn load(&mut self) -> &T::Target { self.load().deref() } } impl From for Cache where A: Deref>, T: RefCnt, S: Strategy, { 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 { inner: Cache, projection: F, } impl Access for MapCache where A: Deref>, T: RefCnt, S: Strategy, F: FnMut(&T) -> &U, { fn load(&mut self) -> &U { (self.projection)(self.inner.load()) } } #[cfg(test)] mod tests { use std::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()); } }