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}