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 std::ops::Deref; 10 use std::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. load(&mut self) -> &T27 fn load(&mut self) -> &T; 28 } 29 30 /// Caching handle for [`ArcSwapAny`][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 /// [Arc]: std::sync::Arc 80 /// [load_full]: ArcSwapAny::load_full 81 #[derive(Clone, Debug)] 82 pub struct Cache<A, T> { 83 arc_swap: A, 84 cached: T, 85 } 86 87 impl<A, T, S> Cache<A, T> 88 where 89 A: Deref<Target = ArcSwapAny<T, S>>, 90 T: RefCnt, 91 S: Strategy<T>, 92 { 93 /// Creates a new caching handle. 94 /// 95 /// The parameter is something dereferencing into an [`ArcSwapAny`] (eg. either to [`ArcSwap`] 96 /// or [`ArcSwapOption`]). That can be [`ArcSwapAny`] itself, but that's not very useful. But 97 /// it also can be a reference to it or `Arc`, which makes it possible to share the 98 /// [`ArcSwapAny`] with multiple caches or access it in non-cached way too. 99 /// 100 /// [`ArcSwapOption`]: crate::ArcSwapOption 101 /// [`ArcSwap`]: crate::ArcSwap new(arc_swap: A) -> Self102 pub fn new(arc_swap: A) -> Self { 103 let cached = arc_swap.load_full(); 104 Self { arc_swap, cached } 105 } 106 107 /// Gives access to the (possibly shared) cached [`ArcSwapAny`]. arc_swap(&self) -> &A::Target108 pub fn arc_swap(&self) -> &A::Target { 109 &self.arc_swap 110 } 111 112 /// Loads the currently held value. 113 /// 114 /// This first checks if the cached value is up to date. This check is very cheap. 115 /// 116 /// If it is up to date, the cached value is simply returned without additional costs. If it is 117 /// outdated, a load is done on the underlying shared storage. The newly loaded value is then 118 /// stored in the cache and returned. 119 #[inline] load(&mut self) -> &T120 pub fn load(&mut self) -> &T { 121 self.revalidate(); 122 self.load_no_revalidate() 123 } 124 125 #[inline] load_no_revalidate(&self) -> &T126 fn load_no_revalidate(&self) -> &T { 127 &self.cached 128 } 129 130 #[inline] revalidate(&mut self)131 fn revalidate(&mut self) { 132 let cached_ptr = RefCnt::as_ptr(&self.cached); 133 // Node: Relaxed here is fine. We do not synchronize any data through this, we already have 134 // it synchronized in self.cache. We just want to check if it changed, if it did, the 135 // load_full will be responsible for any synchronization needed. 136 let shared_ptr = self.arc_swap.ptr.load(Ordering::Relaxed); 137 if cached_ptr != shared_ptr { 138 self.cached = self.arc_swap.load_full(); 139 } 140 } 141 142 /// Turns this cache into a cache with a projection inside the cached value. 143 /// 144 /// You'd use this in case when some part of code needs access to fresh values of `U`, however 145 /// a bigger structure containing `U` is provided by this cache. The possibility of giving the 146 /// whole structure to the part of the code falls short in terms of reusability (the part of 147 /// the code could be used within multiple contexts, each with a bigger different structure 148 /// containing `U`) and code separation (the code shouldn't needs to know about the big 149 /// structure). 150 /// 151 /// # Warning 152 /// 153 /// As the provided `f` is called inside every [`load`][Access::load], this one should be 154 /// cheap. Most often it is expected to be just a closure taking reference of some inner field. 155 /// 156 /// For the same reasons, it should not have side effects and should never panic (these will 157 /// not break Rust's safety rules, but might produce behaviour you don't expect). 158 /// 159 /// # Examples 160 /// 161 /// ```rust 162 /// use arc_swap::ArcSwap; 163 /// use arc_swap::cache::{Access, Cache}; 164 /// 165 /// struct InnerCfg { 166 /// answer: usize, 167 /// } 168 /// 169 /// struct FullCfg { 170 /// inner: InnerCfg, 171 /// } 172 /// 173 /// fn use_inner<A: Access<InnerCfg>>(cache: &mut A) { 174 /// let value = cache.load(); 175 /// println!("The answer is: {}", value.answer); 176 /// } 177 /// 178 /// let full_cfg = ArcSwap::from_pointee(FullCfg { 179 /// inner: InnerCfg { 180 /// answer: 42, 181 /// } 182 /// }); 183 /// let cache = Cache::new(&full_cfg); 184 /// use_inner(&mut cache.map(|full| &full.inner)); 185 /// 186 /// let inner_cfg = ArcSwap::from_pointee(InnerCfg { answer: 24 }); 187 /// let mut inner_cache = Cache::new(&inner_cfg); 188 /// use_inner(&mut inner_cache); 189 /// ``` map<F, U>(self, f: F) -> MapCache<A, T, F> where F: FnMut(&T) -> &U,190 pub fn map<F, U>(self, f: F) -> MapCache<A, T, F> 191 where 192 F: FnMut(&T) -> &U, 193 { 194 MapCache { 195 inner: self, 196 projection: f, 197 } 198 } 199 } 200 201 impl<A, T, S> Access<T::Target> for Cache<A, T> 202 where 203 A: Deref<Target = ArcSwapAny<T, S>>, 204 T: Deref<Target = <T as RefCnt>::Base> + RefCnt, 205 S: Strategy<T>, 206 { load(&mut self) -> &T::Target207 fn load(&mut self) -> &T::Target { 208 self.load().deref() 209 } 210 } 211 212 impl<A, T, S> From<A> for Cache<A, T> 213 where 214 A: Deref<Target = ArcSwapAny<T, S>>, 215 T: RefCnt, 216 S: Strategy<T>, 217 { from(arc_swap: A) -> Self218 fn from(arc_swap: A) -> Self { 219 Self::new(arc_swap) 220 } 221 } 222 223 /// An implementation of a cache with a projection into the accessed value. 224 /// 225 /// This is the implementation structure for [`Cache::map`]. It can't be created directly and it 226 /// should be used through the [`Access`] trait. 227 #[derive(Clone, Debug)] 228 pub struct MapCache<A, T, F> { 229 inner: Cache<A, T>, 230 projection: F, 231 } 232 233 impl<A, T, S, F, U> Access<U> for MapCache<A, T, F> 234 where 235 A: Deref<Target = ArcSwapAny<T, S>>, 236 T: RefCnt, 237 S: Strategy<T>, 238 F: FnMut(&T) -> &U, 239 { load(&mut self) -> &U240 fn load(&mut self) -> &U { 241 (self.projection)(self.inner.load()) 242 } 243 } 244 245 #[cfg(test)] 246 mod tests { 247 use std::sync::Arc; 248 249 use super::*; 250 use crate::{ArcSwap, ArcSwapOption}; 251 252 #[test] cached_value()253 fn cached_value() { 254 let a = ArcSwap::from_pointee(42); 255 let mut c1 = Cache::new(&a); 256 let mut c2 = Cache::new(&a); 257 258 assert_eq!(42, **c1.load()); 259 assert_eq!(42, **c2.load()); 260 261 a.store(Arc::new(43)); 262 assert_eq!(42, **c1.load_no_revalidate()); 263 assert_eq!(43, **c1.load()); 264 } 265 266 #[test] cached_through_arc()267 fn cached_through_arc() { 268 let a = Arc::new(ArcSwap::from_pointee(42)); 269 let mut c = Cache::new(Arc::clone(&a)); 270 assert_eq!(42, **c.load()); 271 a.store(Arc::new(0)); 272 drop(a); // A is just one handle, the ArcSwap is kept alive by the cache. 273 } 274 275 #[test] cache_option()276 fn cache_option() { 277 let a = ArcSwapOption::from_pointee(42); 278 let mut c = Cache::new(&a); 279 280 assert_eq!(42, **c.load().as_ref().unwrap()); 281 a.store(None); 282 assert!(c.load().is_none()); 283 } 284 285 struct Inner { 286 answer: usize, 287 } 288 289 struct Outer { 290 inner: Inner, 291 } 292 293 #[test] map_cache()294 fn map_cache() { 295 let a = ArcSwap::from_pointee(Outer { 296 inner: Inner { answer: 42 }, 297 }); 298 299 let mut cache = Cache::new(&a); 300 let mut inner = cache.clone().map(|outer| &outer.inner); 301 let mut answer = cache.clone().map(|outer| &outer.inner.answer); 302 303 assert_eq!(42, cache.load().inner.answer); 304 assert_eq!(42, inner.load().answer); 305 assert_eq!(42, *answer.load()); 306 307 a.store(Arc::new(Outer { 308 inner: Inner { answer: 24 }, 309 })); 310 311 assert_eq!(24, cache.load().inner.answer); 312 assert_eq!(24, inner.load().answer); 313 assert_eq!(24, *answer.load()); 314 } 315 } 316