engine/store/
macros.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4/// A macro for defining functions whose return values will wrapped in a [`Cache`][super::Cache].
5///
6/// # Example
7/// ```
8/// use engine::cache;
9///
10/// cache! {
11///    fn fib(n: u32) -> u32 => {
12///      match n {
13///         0 => 1,
14///         1 => 1,
15///         _ => fib(n - 1) + fib(n - 2),
16///      }
17///    }
18/// }
19///
20/// assert_eq!(fib(20), 10946);
21/// assert_eq!(FIB_CACHE.lock().unwrap().get(&20), Some(&10946));
22/// ```
23#[macro_export]
24macro_rules! cache {
25    (fn $name:ident ($($arg:ident: $arg_type:ty), *) -> $ret:ty => $body:expr) => {
26        use once_cell::sync::Lazy;
27        use std::sync::Mutex;
28        use engine::store::Cache;
29        use paste::paste;
30
31        paste! {
32            // create a static instance of `Cache<K, V>` for the expression called `$EXPR_NAME_CACHE`.
33            static [<$name:upper _CACHE>]: Lazy<Mutex<Cache<($($arg_type),*), $ret>>> = Lazy::new(|| Mutex::new(Cache::new()));
34
35            #[allow(unused_parens)]
36            fn $name($($arg: $arg_type), *) -> $ret {
37                // create a static instance of `Cache<K, V>`
38                // static CACHE: Lazy<Mutex<Cache<($($arg_type),*), $ret>>> =
39                //     Lazy::new(|| Mutex::new(Cache::new()));
40
41                // create key out of arg.
42                let key = ($($arg.clone()), *);
43
44                // get mutex to check for cached value.
45                let cache = [<$name:upper _CACHE>].lock().unwrap();
46
47                // check for cached value.
48                match cache.get(&key) {
49                    // if value is cached, return it.
50                    Some(val) => val.clone(),
51                    None => {
52                        // drop the mutex before execution to avoid a deadlock.
53                        drop(cache);
54                        // execute the body of the function/expression.
55                        let value = (||$body)();
56
57                        // re-get mutex to add/update the cache.
58                        let mut cache = [<$name:upper _CACHE>].lock().unwrap();
59                        cache.insert(key, value, None);
60                        value.clone()
61                    }
62                }
63            }
64        }
65    };
66}