1macro_rules! doc_comment {
2 ($($contents:expr)+ => $($item:tt)+) => {
3 doc_comment! {@ concat!($($contents),+), $($item)+ }
4 };
5 (@ $contents:expr, $($item:tt)+) => {
6 #[doc = $contents]
7 $($item)+
8 };
9}
10
11macro_rules! impl_common_traits_for_type_with_addr {
12 ($type_:ty) => {
13 impl Hash for $type_ {
14 fn hash<H>(&self, hasher: &mut H)
15 where
16 H: Hasher,
17 {
18 self.addr().hash(hasher);
19 }
20 }
21
22 impl PartialEq for $type_ {
23 fn eq(&self, other: &Self) -> bool {
24 self.addr().eq(&other.addr())
25 }
26 }
27 impl Eq for $type_ {}
28
29 impl PartialOrd for $type_ {
30 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
31 self.addr().partial_cmp(&other.addr())
32 }
33 }
34
35 impl Ord for $type_ {
36 fn cmp(&self, other: &Self) -> Ordering {
37 self.addr().cmp(&other.addr())
38 }
39 }
40 };
41}
42
43macro_rules! define_cache {
44 ($module:ident, $cache:ident $(: $bound:ident)?, $($rest:tt)*) => {
45paste::item! {
46 define_cache! {@
47 $module,
48 $cache $(: $bound)?,
49 [<$cache:snake _tests>],
50 [<Shared $cache>],
51 $($rest)*
52 }
53}
54 };
55 (@
56 $module:ident,
57 $cache:ident $(: $bound:ident)?,
58 $test_mod:ident,
59 $shared:ident,
60 $refct:ident,
61 $lock:ident :: $acquire:ident
62 ) => {
63use crate::{dep_node::Dependent, *};
64use hash_hasher::HashBuildHasher;
65use hashbrown::HashMap;
66use std::{any::TypeId, borrow::Borrow, cmp::{Eq, Ordering}, hash::{Hash, Hasher}};
67
68doc_comment! {"
69Holds arbitrary query results which are namespaced by arbitrary scope types. Usually used
70through [`" stringify!($shared) "::cache_with`] and [`" stringify!($shared) "::gc`].
71
72# Query types
73
74> Note: the types referenced in this documentation are only visible on individual methods, as
75> `" stringify!($cache) "` is not itself a generic type.
76
77Storage is sharded by the type of the query. The type of a query has three parts:
78
79The query scope is the value which indexes the storage for a particular query type, it has the
80bound `Scope: 'static + Eq + Hash" $(" + " stringify!($bound))? "`.
81
82Each `Scope` corresponds to at most a single `Input: 'static" $(" + " stringify!($bound))? "`
83and a single `Output: 'static" $(" + " stringify!($bound))? "` value at any given time.
84
85# Reading stored values
86
87See [`" stringify!($cache) "::get`] which accepts borrowed forms of `Scope`
88and `Input`: `Key` and `Arg` respectively. `Arg` must satisfy `PartialEq<Input>` to determine
89whether to return a stored output.
90
91# Garbage Collection
92
93Each time [`" stringify!($cache) "::gc`] is called it removes any values which haven't been
94referenced since the prior call.
95
96After each GC, all values still in the cache are marked garbage. They are marked live again when
97inserted with [`" stringify!($cache) "::store`] or read with
98[`" stringify!($cache) "::get`].
99"=>
100#[derive(Debug, Default)]
101pub struct $cache {
102 inner: HashMap<TypeId, Box<dyn Storage $(+ $bound)?>, HashBuildHasher>,
105 revision: u64,
106}}
107
108impl $cache {
109doc_comment! {"
110Return a reference to a query's stored output if a result is stored *and* `arg` equals the
111previously-stored `Input`. If a reference is returned, the stored input/output
112is marked as a root and will not be GC'd the next call.
113
114If no reference is found, a [`CacheMiss`] is returned. Call [`CacheMiss::init`] to get
115a [`CacheEntry`] to pass to [`" stringify!($cache) "::store`].
116"=>
117 pub fn get<'k, Key, Scope, Arg, Input, Output>(
118 &self,
119 key: &'k Key,
120 arg: &Arg,
121 ) -> Result<&Output, CacheMiss<'k, Key, Scope, Input, Output>>
122 where
123 Key: Eq + Hash + ToOwned<Owned = Scope> + ?Sized,
124 Scope: 'static + Borrow<Key> + Eq + Hash,
125 Arg: PartialEq<Input> + ToOwned<Owned=Input> + ?Sized,
126 Input: 'static + Borrow<Arg>,
127 Output: 'static,
128 {
129 let dependent = Dependent::incoming();
130 let query = Query::new(self.inner.hasher());
131
132 if let Some(ns) = self.get_namespace(&query) {
133 ns.get(key, arg, dependent, self.revision)
134 .map_err(|key_miss| CacheMiss { query, key_miss })
135 } else {
136 let key_miss = KeyMiss::just_key(key, arg.to_owned(), dependent, self.revision);
137 Err(CacheMiss { query, key_miss, })
138 }
139 }}
140
141doc_comment! {"
142Stores a fresh [`CacheEntry`] whose input/output will not be GC'd at the next call.
143Call [`" stringify!($cache) "::get`] to get a [`CacheMiss`] and [`CacheMiss::init`] to get a
144[`CacheEntry`].
145 "=>
146 pub fn store<Key, Scope, Input, Output>(
147 &mut self,
148 entry: CacheEntry<'_, Key, Scope, Input, Output>,
149 ) where
150 Key: Eq + Hash + ToOwned<Owned = Scope> + ?Sized,
151 Scope: 'static + Borrow<Key> + Eq + Hash $(+ $bound)?,
152 Input: 'static $(+ $bound)?,
153 Output: 'static $(+ $bound)?,
154 {
155 let revision = self.revision; let CacheEntry {
157 miss: CacheMiss { query, key_miss },
158 output,
159 } = entry;
160 self.get_namespace_mut(&query).store(key_miss, output, revision);
161 }}
162
163 fn get_namespace<Scope, Input, Output>(
164 &self,
165 query: &Query<Scope, Input, Output>,
166 ) -> Option<&Namespace<Scope, Input, Output>>
167 where
168 Scope: 'static,
169 Input: 'static,
170 Output: 'static,
171 {
172 let gc: &dyn Storage = &**self
173 .inner
174 .raw_entry()
175 .from_hash(query.hash(), |t| t == &query.ty())?.1;
176 Some(gc.as_any().downcast_ref().unwrap())
177 }
178
179 fn get_namespace_mut<Scope, Input, Output>(
180 &mut self,
181 query: &Query<Scope, Input, Output>,
182 ) -> &mut Namespace<Scope, Input, Output>
183 where
184 Scope: 'static + Eq + Hash $(+ $bound)?,
185 Input: 'static $(+ $bound)?,
186 Output: 'static $(+ $bound)?,
187 {
188 let gc: &mut dyn Storage = &mut **self
189 .inner
190 .raw_entry_mut()
191 .from_hash(query.hash(), |t| t == &query.ty())
192 .or_insert_with(|| {
193 (query.ty(), query.make_namespace())
194 }).1;
195 gc.as_any_mut().downcast_mut().unwrap()
196 }
197
198 pub fn gc(&mut self) {
200 let prev = self.revision; self.inner.values_mut().for_each(|ns| ns.mark(prev));
202 self.inner.values_mut().for_each(|namespace| namespace.sweep());
203 self.revision += 1;
204 }
205}
206
207impl std::panic::UnwindSafe for $cache {}
208impl std::panic::RefUnwindSafe for $cache {}
209
210doc_comment! {"
211Provides shared, synchronized access to a [`" stringify!($cache) "`] and a function-memoization
212API in [`" stringify!($shared) "::cache_with`].
213
214For convenience wrappers around [`" stringify!($shared) "::cache_with`] see
215[`" stringify!($shared) "::cache`] for returned types that implement
216`Clone` and [`" stringify!($shared) "::hold`] for values that just need to be stored
217without returning a reference.
218
219# Example
220
221```
222let storage = dyn_cache::" stringify!($module) "::" stringify!($shared) r#"::default();
223let call_count = std::cell::Cell::new(0);
224let increment_count = |&to_add: &i32| {
225 let new_count = call_count.get() + to_add;
226 call_count.set(new_count);
227 new_count
228};
229
230assert_eq!(call_count.get(), 0, "not called yet");
231
232let with_one = storage.cache_with(&'a', &1, &increment_count, Clone::clone);
233assert_eq!(call_count.get(), 1, "called only once");
234assert_eq!(call_count.get(), with_one);
235
236let with_one_again = storage.cache_with(&'a', &1, &increment_count, Clone::clone);
237assert_eq!(call_count.get(), 1, "still called only once, previous value cached");
238assert_eq!(call_count.get(), with_one_again);
239
240let with_two = storage.cache_with(&'a', &2, &increment_count, Clone::clone);
241assert_eq!(call_count.get(), 3, "called again with a new, larger increment");
242assert_eq!(call_count.get(), with_two);
243
244let with_other_query = storage.cache_with(&'b', &1, &increment_count, Clone::clone);
245assert_eq!(call_count.get(), 4, "called again with the same increment, different scope");
246assert_eq!(call_count.get(), with_other_query);
247
248let with_two_again = storage.cache_with(&'a', &2, &increment_count, Clone::clone);
249assert_eq!(call_count.get(), 4, "cell still has last mutation's value");
250assert_eq!(with_two_again, with_two, "cache should still have previous value");
251
252storage.gc(); // won't drop any values, but sets all of the cached values to be dropped
253call_count.set(0);
254
255// re-run 'b', marking it live
256let reran_other_query = storage.cache_with(&'b', &1, &increment_count, Clone::clone);
257assert_eq!(reran_other_query , 4, "returns the cached value");
258assert_eq!(call_count.get(), 0, "without running increment_count");
259
260storage.gc(); // query 'a' will be dropped
261
262// re-run 'b', observing cached value
263let reran_other_query = storage.cache_with(&'b', &1, &increment_count, Clone::clone);
264assert_eq!(reran_other_query , 4, "still returns the cached value");
265assert_eq!(call_count.get(), 0, "still without running increment_count");
266
267// run 'a' again, observe no cached value
268let with_one_again = storage.cache_with(&'a', &1, &increment_count, Clone::clone);
269assert_eq!(call_count.get(), 1, "called without caching");
270assert_eq!(call_count.get(), with_one_again);
271```
272"#=>
273#[derive(Clone, Debug, Default)]
274pub struct $shared {
275 inner: $refct<$lock<$cache>>,
276}}
277
278impl $shared {
279doc_comment!{r"
280Caches the result of `init(arg)` once per `key`, re-running it when `arg` changes. Always
281runs `with` on the stored `Output` before returning the result.
282
283See [`" stringify!($shared) "::cache`] for an ergonomic wrapper that requires `Output: Clone`.
284"=>
285 pub fn cache_with<Key, Scope, Arg, Input, Output, Ret>(
286 &self,
287 key: &Key,
288 arg: &Arg,
289 init: impl FnOnce(&Input) -> Output,
290 with: impl FnOnce(&Output) -> Ret,
291 ) -> Ret
292 where
293 Key: Eq + Hash + ToOwned<Owned = Scope> + ?Sized,
294 Scope: 'static + Borrow<Key> + Eq + Hash $(+ $bound)?,
295 Arg: PartialEq<Input> + ToOwned<Owned=Input> + ?Sized,
296 Input: 'static + Borrow<Arg> $(+ $bound)?,
297 Output: 'static $(+ $bound)?,
298 Ret: 'static $(+ $bound)?,
299 {
300 let miss = match { self.inner.$acquire().get(key, arg) } {
301 Ok(stored) => return with(stored),
302 Err(m) => m,
303 };
304
305 let (to_store, to_return) = miss.init(|arg| {
306 let store = init(arg);
307 let ret = with(&store);
308 (store, ret)
309 });
310
311 self.inner.$acquire().store(to_store);
312 to_return
313 }}
314
315doc_comment!{r"
316Caches the result of `init(arg)` once per `key`, re-running it when `arg` changes. Clones
317the cached output before returning the result.
318
319See [`" stringify!($shared) "::cache_with`] for a lower-level version which does not require
320`Output: Clone`.
321"=>
322 pub fn cache<Key, Scope, Arg, Input, Output>(
323 &self,
324 key: &Key,
325 arg: &Arg,
326 init: impl FnOnce(&Input) -> Output,
327 ) -> Output
328 where
329 Key: Eq + Hash + ToOwned<Owned = Scope> + ?Sized,
330 Scope: 'static + Borrow<Key> + Eq + Hash $(+ $bound)?,
331 Arg: PartialEq<Input> + ToOwned<Owned=Input> + ?Sized,
332 Input: 'static + Borrow<Arg> $(+ $bound)?,
333 Output: 'static + Clone $(+ $bound)?,
334 {
335 self.cache_with(key, arg, init, Clone::clone)
336 }}
337
338doc_comment!{r"
339Caches the result of `init(arg)` once per `key`, re-running it when `arg` changes.
340
341Does not return any reference to the cached value. See [`" stringify!($shared) "::cache`]
342for similar functionality that returns a copy of `Output` or
343[`" stringify!($shared) "::cache_with`] which allows specifying other pre-return functions.
344"=>
345 pub fn hold<Key, Scope, Arg, Input, Output>(
346 &self,
347 key: &Key,
348 arg: &Arg,
349 init: impl FnOnce(&Input) -> Output,
350 )
351 where
352 Key: Eq + Hash + ToOwned<Owned = Scope> + ?Sized,
353 Scope: 'static + Borrow<Key> + Eq + Hash $(+ $bound)?,
354 Arg: PartialEq<Input> + ToOwned<Owned=Input> + ?Sized,
355 Input: 'static + Borrow<Arg> $(+ $bound)?,
356 Output: 'static $(+ $bound)?,
357 {
358 self.cache_with(key, arg, init, |_| {})
359 }}
360
361doc_comment!{"
362Forwards to [`" stringify!($cache) "::gc`].
363"=>
364 pub fn gc(&self) {
365 self.inner.$acquire().gc();
366 }}
367
368 fn addr(&self) -> usize {
369 $refct::as_ptr(&self.inner) as *const _ as _
370 }
371}
372
373impl_common_traits_for_type_with_addr!($shared);
374
375impl From<$cache> for $shared {
376 fn from(inner: $cache) -> Self {
377 Self { inner: $refct::new($lock::new(inner)) }
378 }
379}
380
381impl std::panic::UnwindSafe for $shared {}
382impl std::panic::RefUnwindSafe for $shared {}
383
384#[cfg(test)]
385mod $test_mod {
386 use super::*;
387 use std::sync::{
388 atomic::{AtomicU32, Ordering},
389 Arc,
390 };
391 use parking_lot::Mutex;
392
393 #[test]
394 fn single_query_with_gc() {
395 let storage = $shared::default();
396 let call_count = std::cell::Cell::new(0);
397 let increment_count = |&to_add: &i32| {
398 let new_count = call_count.get() + to_add;
399 call_count.set(new_count);
400 new_count
401 };
402
403 assert_eq!(call_count.get(), 0);
404
405 let with_b = storage.cache_with(&'b', &1, &increment_count, Clone::clone);
406 assert_eq!(call_count.get(), 1);
407 assert_eq!(call_count.get(), with_b);
408
409 storage.gc(); call_count.set(0);
411
412 let rerun_b = storage.cache_with(&'b', &1, &increment_count, Clone::clone);
413 assert_eq!(rerun_b , 1);
414 assert_eq!(call_count.get(), 0);
415
416 storage.gc();
417 storage.gc();
419
420 let again = storage.cache_with(&'b', &1, &increment_count, Clone::clone);
421 assert_eq!(again, 1);
422 assert_eq!(call_count.get(), 1);
423 }
424
425 #[test]
426 fn distinct_scopes_distinct_storage() {
427 let storage = $shared::default();
428 let call_count = std::cell::Cell::new(0);
429 let increment_count = |&to_add: &i32| {
430 let new_count = call_count.get() + to_add;
431 call_count.set(new_count);
432 new_count
433 };
434
435 assert_eq!(call_count.get(), 0);
436
437 let a_with_1 = storage.cache_with(&'a', &1, &increment_count, Clone::clone);
438 assert_eq!(call_count.get(), 1);
439 assert_eq!(call_count.get(), a_with_1);
440
441 let b_with_1 = storage.cache_with(&'b', &1, &increment_count, Clone::clone);
442 assert_eq!(call_count.get(), 2);
443 assert_eq!(call_count.get(), b_with_1);
444
445 let a_with_1_again = storage.cache_with(&'a', &1, &increment_count, Clone::clone);
446 assert_eq!(call_count.get(), 2, "untouched");
447 assert_eq!(a_with_1_again, a_with_1, "cached");
448
449 let with_a_2 = storage.cache_with(&'a', &2, &increment_count, Clone::clone);
450 assert_eq!(call_count.get(), 4);
451 assert_eq!(call_count.get(), with_a_2);
452
453 let with_a_2_again = storage.cache_with(&'a', &2, &increment_count, Clone::clone);
454 assert_eq!(call_count.get(), 4);
455 assert_eq!(with_a_2_again, with_a_2);
456 }
457
458 #[test]
459 fn hold_retains_across_gcs() {
460 let storage = $shared::default();
461
462 let guard_count_inc = Arc::new(Mutex::new(0));
463 let drop_count_inc = Arc::new(Mutex::new(0));
464 let (guard_count, drop_count) = (guard_count_inc.clone(), drop_count_inc.clone());
465
466 macro_rules! assert_counts {
467 ($guard:expr, $drop:expr) => {{
468 assert_eq!($guard, *guard_count.lock());
469 assert_eq!($drop, *drop_count.lock());
470 }};
471 }
472
473 let make_guard = || {
474 let (guard_count_inc, drop_count_inc) = (
475 guard_count_inc.clone(),
476 drop_count_inc.clone(),
477 );
478 storage.hold(
479 &'a',
480 &(),
481 move |&()| {
482 *guard_count_inc.lock() += 1;
483 scopeguard::guard((), move |()| *drop_count_inc.lock() += 1)
484 },
485 );
486 };
487
488 assert_counts!(0, 0);
489 make_guard();
490 assert_counts!(1, 0);
491 storage.gc();
492 assert_counts!(1, 0);
493 make_guard();
494 assert_counts!(1, 0);
495 storage.gc();
496 assert_counts!(1, 0);
497 storage.gc();
498 assert_counts!(1, 1);
499 make_guard();
500 assert_counts!(2, 1);
501 }
502
503 #[test]
504 fn nested_hold_retains_across_gcs() {
505 let storage = $shared::default();
506
507 let guard_count_inc = Arc::new(Mutex::new(0));
508 let drop_count_inc = Arc::new(Mutex::new(0));
509 let (guard_count, drop_count) = (guard_count_inc.clone(), drop_count_inc.clone());
510
511 macro_rules! assert_counts {
512 ($guard:expr, $drop:expr) => {{
513 assert_eq!($guard, *guard_count.lock(), "guard count incorrect");
514 assert_eq!($drop, *drop_count.lock(), "drop count incorrect");
515 }};
516 }
517
518 let make_guard = || {
519 let (guard_count_inc, drop_count_inc) = (
520 guard_count_inc.clone(),
521 drop_count_inc.clone(),
522 );
523 storage.hold(
524 &'a',
525 &(),
526 |&()| {
527 *guard_count_inc.lock() += 1;
528 scopeguard::guard((), move |()| *drop_count_inc.lock() += 1)
529 },
530 );
531 };
532
533 let memo_make_guard = || {
534 storage.hold("foo", "bar", |_| make_guard());
535 };
536
537 let memo_memo_make_guard = || {
538 storage.hold("baz", "quux", |_| memo_make_guard());
539 };
540
541 assert_counts!(0, 0);
542 memo_memo_make_guard();
543 assert_counts!(1, 0);
544 storage.gc();
545 assert_counts!(1, 0);
546 memo_memo_make_guard();
547 assert_counts!(1, 0);
548 storage.gc();
549 assert_counts!(1, 0);
550 storage.gc();
551 assert_counts!(1, 1); }
553
554 struct CountDrops {
555 num_drops: Arc<AtomicU32>,
556 }
557
558 impl Drop for CountDrops {
559 fn drop(&mut self) {
560 self.num_drops.fetch_add(1, Ordering::SeqCst);
561 }
562 }
563
564 #[test]
565 fn issue_238_cache_call_incorrectly_preserves_child() {
566 let once_calls = Arc::new(AtomicU32::new(0));
568
569 let once_drops = Arc::new(AtomicU32::new(0));
571
572 let mut storage = $shared::default();
573
574 let mut i = 0; let i = &mut i;
576 let adder = once_calls.clone();
577 let drop_adder = once_drops.clone();
578 let mut tick = move |should_hold, storage: &mut $shared| {
579 storage.cache_with(&(), &*i, |_| {
580 if should_hold {
581 storage.hold(&(), &(), |_| {
582 adder.fetch_add(1, Ordering::SeqCst);
583 Arc::new(CountDrops { num_drops: drop_adder.clone() })
584 });
585 }
586 }, |_| {});
587 storage.gc();
588 *i += 1;
589 };
590
591 tick(false, &mut storage);
592 assert_eq!(once_calls.load(Ordering::SeqCst), 0);
593 assert_eq!(once_drops.load(Ordering::SeqCst), 0);
594
595 tick(true, &mut storage);
596 assert_eq!(once_calls.load(Ordering::SeqCst), 1);
597 assert_eq!(once_drops.load(Ordering::SeqCst), 0);
598
599 tick(false, &mut storage);
600 assert_eq!(once_calls.load(Ordering::SeqCst), 1);
601 assert_eq!(once_drops.load(Ordering::SeqCst), 1);
602 }
603}
604 };
605}