blob: dbb7e64eb8a4ab81d00256dc3f8c2c73dcbcda8d [file] [log] [blame]
Chih-Hung Hsiehe42c5052020-04-16 10:44:21 -07001// 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
11pub use self::imp::{Cached, CachedGuard};
12
13#[cfg(feature = "perf-cache")]
14mod 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> {
24 pub fn new() -> Cached<T> {
25 Cached(CachedThreadLocal::new())
26 }
27
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> {
34 pub fn value(&self) -> &T {
35 self.0
36 }
37 }
38}
39
40#[cfg(not(feature = "perf-cache"))]
41mod 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> {
69 pub fn new() -> Cached<T> {
70 Cached { stack: Mutex::new(vec![]), _phantom: PhantomData }
71 }
72
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
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> {
88 pub fn value(&self) -> &T {
89 self.value.as_ref().unwrap()
90 }
91 }
92
93 impl<'a, T: Send> Drop for CachedGuard<'a, T> {
94 fn drop(&mut self) {
95 if let Some(value) = self.value.take() {
96 self.cache.put(value);
97 }
98 }
99 }
100}