1 // This module defines a common API for caching internal runtime state. 2 // The `thread_local` crate provides an extremely optimized version of this. 3 // However, if the perf-cache feature is disabled, then we drop the 4 // thread_local dependency and instead use a pretty naive caching mechanism 5 // with a mutex. 6 // 7 // Strictly speaking, the CachedGuard isn't necessary for the much more 8 // flexible thread_local API, but implementing thread_local's API doesn't 9 // seem possible in purely safe code. 10 11 pub use self::imp::{Cached, CachedGuard}; 12 13 #[cfg(feature = "perf-cache")] 14 mod imp { 15 use thread_local::CachedThreadLocal; 16 17 #[derive(Debug)] 18 pub struct Cached<T: Send>(CachedThreadLocal<T>); 19 20 #[derive(Debug)] 21 pub struct CachedGuard<'a, T: 'a>(&'a T); 22 23 impl<T: Send> Cached<T> { new() -> Cached<T>24 pub fn new() -> Cached<T> { 25 Cached(CachedThreadLocal::new()) 26 } 27 get_or(&self, create: impl FnOnce() -> T) -> CachedGuard<T>28 pub fn get_or(&self, create: impl FnOnce() -> T) -> CachedGuard<T> { 29 CachedGuard(self.0.get_or(|| create())) 30 } 31 } 32 33 impl<'a, T: Send> CachedGuard<'a, T> { value(&self) -> &T34 pub fn value(&self) -> &T { 35 self.0 36 } 37 } 38 } 39 40 #[cfg(not(feature = "perf-cache"))] 41 mod imp { 42 use std::marker::PhantomData; 43 use std::panic::UnwindSafe; 44 use std::sync::Mutex; 45 46 #[derive(Debug)] 47 pub struct Cached<T: Send> { 48 stack: Mutex<Vec<T>>, 49 /// When perf-cache is enabled, the thread_local crate is used, and 50 /// its CachedThreadLocal impls Send, Sync and UnwindSafe, but NOT 51 /// RefUnwindSafe. However, a Mutex impls RefUnwindSafe. So in order 52 /// to keep the APIs consistent regardless of whether perf-cache is 53 /// enabled, we force this type to NOT impl RefUnwindSafe too. 54 /// 55 /// Ideally, we should always impl RefUnwindSafe, but it seems a little 56 /// tricky to do that right now. 57 /// 58 /// See also: https://github.com/rust-lang/regex/issues/576 59 _phantom: PhantomData<Box<dyn Send + Sync + UnwindSafe>>, 60 } 61 62 #[derive(Debug)] 63 pub struct CachedGuard<'a, T: 'a + Send> { 64 cache: &'a Cached<T>, 65 value: Option<T>, 66 } 67 68 impl<T: Send> Cached<T> { new() -> Cached<T>69 pub fn new() -> Cached<T> { 70 Cached { stack: Mutex::new(vec![]), _phantom: PhantomData } 71 } 72 get_or(&self, create: impl FnOnce() -> T) -> CachedGuard<T>73 pub fn get_or(&self, create: impl FnOnce() -> T) -> CachedGuard<T> { 74 let mut stack = self.stack.lock().unwrap(); 75 match stack.pop() { 76 None => CachedGuard { cache: self, value: Some(create()) }, 77 Some(value) => CachedGuard { cache: self, value: Some(value) }, 78 } 79 } 80 put(&self, value: T)81 fn put(&self, value: T) { 82 let mut stack = self.stack.lock().unwrap(); 83 stack.push(value); 84 } 85 } 86 87 impl<'a, T: Send> CachedGuard<'a, T> { value(&self) -> &T88 pub fn value(&self) -> &T { 89 self.value.as_ref().unwrap() 90 } 91 } 92 93 impl<'a, T: Send> Drop for CachedGuard<'a, T> { drop(&mut self)94 fn drop(&mut self) { 95 if let Some(value) = self.value.take() { 96 self.cache.put(value); 97 } 98 } 99 } 100 } 101