diff options
author | tcmal <tcmal> | 2023-06-16 14:19:58 +0000 |
---|---|---|
committer | Aria <me@aria.rip> | 2023-10-01 17:31:30 +0100 |
commit | ad55a4b05ac35cc21c64c1dd62bff9faec775eb6 (patch) | |
tree | c590e0584e2fb301d0e6c84f60d1d6b42d0c9912 /incria/src/thunk.rs | |
parent | 3f2937c15aec73120dd4d57ddd245ac9ab789090 (diff) |
factor out common code for lazy mappings
Diffstat (limited to 'incria/src/thunk.rs')
-rw-r--r-- | incria/src/thunk.rs | 114 |
1 files changed, 20 insertions, 94 deletions
diff --git a/incria/src/thunk.rs b/incria/src/thunk.rs index f076ba1..937f109 100644 --- a/incria/src/thunk.rs +++ b/incria/src/thunk.rs @@ -1,111 +1,37 @@ //! A mapper based on a function from keys to values. -use std::{collections::HashMap, hash::Hash, pin::Pin, sync::Arc}; +use std::pin::Pin; -use tokio::sync::{Mutex, Notify, RwLock, RwLockReadGuard}; +use crate::lazy_mapping::{LazyMapper, LazyMappingBackend}; -use crate::{ - deps::{self, NodeId}, - Mapper, -}; +/// A function from keys to values. +/// +/// Should be pure, except for use of other mappings. This ensures recomputation is done when needed. +pub trait Thunk<K, V>: Send + 'static { + fn compute(&self, key: K) -> Pin<Box<dyn std::future::Future<Output = V> + Send + '_>>; +} -/// A mapping that lazily computes values with a given async thunk and memoizes them. #[derive(Debug, Default)] -pub struct ThunkMapper<K, V, T> { - /// The thunk we use to compute values +pub struct ThunkBackend<T> { thunk: T, - - /// Map of all values we've calculated already - calculated: RwLock<HashMap<K, (V, NodeId)>>, - - /// Values we're currently calculating - /// The Notify will be triggered when computation is done. - waiting: Mutex<HashMap<K, Arc<Notify>>>, } -impl<K: Clone + Eq + Hash + Send + Sync, V: Send + Sync, T: Thunk<K, V>> ThunkMapper<K, V, T> { - /// Create a new instance of the computing store. +impl<T> ThunkBackend<T> { pub fn new(thunk: T) -> Self { - Self { - thunk, - calculated: RwLock::default(), - waiting: Mutex::default(), - } + Self { thunk } } } -impl<K: Clone + Eq + Hash + Send + Sync, V: Send + Sync, C: Thunk<K, V>> Mapper - for ThunkMapper<K, V, C> +impl<K, V: 'static, T> LazyMappingBackend<K, V> for ThunkBackend<T> +where + T: Thunk<K, V>, { - type Key = K; - type Value = V; - type Wrapper<'a> = RwLockReadGuard<'a, V> where V: 'a; - - async fn get<'a>(&'a self, key: &Self::Key) -> Self::Wrapper<'a> { - // Attempt to reuse or evict the existing value - let mut reuse_dep_id = None; - { - let finished = self.calculated.read().await; - if let Some((_, dep)) = finished.get(key) { - if !deps::is_dirty(*dep) { - deps::add_dep(*dep); - return RwLockReadGuard::map(finished, |hm| &hm.get(key).unwrap().0); - } else { - reuse_dep_id = Some(*dep); - drop(finished); - // Dirty, so we'll recompute below but we should remove it now - if self.calculated.write().await.remove(key).is_none() { - // Someone else already noticed it was dirty and removed it before us, so we need to deal with that - todo!("dirty value removed between us noticing and us doing something") - } - } - } - } - - let barrier = self.waiting.lock().await.get(key).cloned(); - if let Some(barrier) = barrier { - // Waiting for completion - barrier.notified().await; - - let val = RwLockReadGuard::map(self.calculated.read().await, |hm| hm.get(key).unwrap()); - deps::add_dep(val.1); - - return RwLockReadGuard::map(val, |inf| &inf.0); - } else { - // Needs calculated - let notify = Arc::new(Notify::new()); - self.waiting - .lock() - .await - .insert(key.clone(), notify.clone()); - - let dep = if let Some(x) = reuse_dep_id { - deps::clear(x); - x - } else { - deps::next_node_id() - }; - - let val = deps::with_node_id(dep, self.thunk.compute(key.clone())).await; - deps::add_dep(dep); - - self.calculated - .write() - .await - .insert(key.clone(), (val, dep)); - self.waiting.lock().await.remove(key); - - notify.notify_waiters(); + async fn is_dirty(&self, _: &K) -> bool { + false // only dirty if marked dirty by someting else + } - return RwLockReadGuard::map(self.calculated.read().await, |hm| { - &hm.get(key).unwrap().0 - }); - } + fn compute(&self, key: K) -> impl std::future::Future<Output = V> + Send + '_ { + self.thunk.compute(key) } } -/// A function from keys to values. -/// -/// Should be pure, except for use of other mappings. This ensures recomputation is done when needed. -pub trait Thunk<K, V>: Send + 'static { - fn compute(&self, key: K) -> Pin<Box<dyn std::future::Future<Output = V> + Send + '_>>; -} +pub type ThunkMapper<K, V, T> = LazyMapper<K, V, ThunkBackend<T>>; |