regex/
cache.rs

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
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(|| Box::new(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}