diff options
author | tcmal <tcmal> | 2023-05-24 19:13:34 +0000 |
---|---|---|
committer | Aria <me@aria.rip> | 2023-10-01 17:31:29 +0100 |
commit | 2c78bb68b9ff51cfb24ce1b7e111339ab5b37746 (patch) | |
tree | 6d33270f3bbf561343d9a300bc3dcede4518f115 /incria/src | |
parent | f31fcefd85cb69f0c7aa8922f3a579a86eac415d (diff) |
update name and write readme
Diffstat (limited to 'incria/src')
-rw-r--r-- | incria/src/deps.rs | 12 | ||||
-rw-r--r-- | incria/src/lib.rs | 5 | ||||
-rw-r--r-- | incria/src/thunk.rs | 31 |
3 files changed, 31 insertions, 17 deletions
diff --git a/incria/src/deps.rs b/incria/src/deps.rs index 84411c0..d72e684 100644 --- a/incria/src/deps.rs +++ b/incria/src/deps.rs @@ -13,10 +13,10 @@ use std::{ collections::{HashMap, VecDeque}, fmt::Write, future::Future, + sync::Mutex, }; use once_cell::sync::OnceCell; -use parking_lot::Mutex; /// Identifier of a node, unique across a program run. pub type NodeId = usize; @@ -42,6 +42,7 @@ impl DepTracker { fn add_dep(&self, dep: NodeId) { self.deps .lock() + .unwrap() .entry(NODE_ID.get()) .and_modify(|v| v.deps.push(dep)) .or_insert(NodeInfo { @@ -52,14 +53,14 @@ impl DepTracker { /// See [`self::next_node_id`] fn next_node_id(&self) -> usize { - let mut lock = self.next_node_id.lock(); + let mut lock = self.next_node_id.lock().unwrap(); *lock += 1; *lock - 1 } /// See [`self::mark_dirty`] fn mark_dirty(&self, node: NodeId) { - let mut lock = self.deps.lock(); + let mut lock = self.deps.lock().unwrap(); let mut frontier = VecDeque::new(); frontier.push_back(node); while let Some(node_id) = frontier.pop_front() { @@ -85,6 +86,7 @@ impl DepTracker { fn is_dirty(&self, node: NodeId) -> bool { self.deps .lock() + .unwrap() .get(&node) .map(|v| v.dirty) .unwrap_or(false) @@ -92,7 +94,7 @@ impl DepTracker { /// See [`self::clear`] fn clear(&self, node: NodeId) { - let mut lock = self.deps.lock(); + let mut lock = self.deps.lock().unwrap(); let node = match lock.get_mut(&node) { Some(x) => x, None => return, @@ -154,7 +156,7 @@ pub fn clear(dep: NodeId) { /// Dirty nodes will be coloured grey. pub fn dep_graphviz() -> String { let mut out = String::new(); - let deps = dep_tracker().deps.lock(); + let deps = dep_tracker().deps.lock().unwrap(); writeln!(&mut out, "digraph G {{").unwrap(); for (id, info) in deps.iter() { if *id == 0 { diff --git a/incria/src/lib.rs b/incria/src/lib.rs index 2f52fbb..c8c35d6 100644 --- a/incria/src/lib.rs +++ b/incria/src/lib.rs @@ -1,4 +1,9 @@ #![feature(async_fn_in_trait)] +//! Incria is a library for incremental computation. +//! It lets you record what a calculation depends on and then only re-run that calculation once one of those dependencies has changed. +//! +//! This is similar to the [query system used by the Rust compiler](https://rustc-dev-guide.rust-lang.org/query.html), but implemented using async. +//! By seperating the tracking of dependencies from the calculation logic, you can write clean code that re-runs only when it needs to, and can be parallelised with little extra work. pub mod deps; mod mapping; diff --git a/incria/src/thunk.rs b/incria/src/thunk.rs index 6aa44dd..f076ba1 100644 --- a/incria/src/thunk.rs +++ b/incria/src/thunk.rs @@ -1,8 +1,7 @@ //! A mapper based on a function from keys to values. use std::{collections::HashMap, hash::Hash, pin::Pin, sync::Arc}; -use parking_lot::{MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard}; -use tokio::sync::Notify; +use tokio::sync::{Mutex, Notify, RwLock, RwLockReadGuard}; use crate::{ deps::{self, NodeId}, @@ -39,13 +38,13 @@ impl<K: Clone + Eq + Hash + Send + Sync, V: Send + Sync, C: Thunk<K, V>> Mapper { type Key = K; type Value = V; - type Wrapper<'a> = MappedRwLockReadGuard<'a, V> where V: 'a; + 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(); + let finished = self.calculated.read().await; if let Some((_, dep)) = finished.get(key) { if !deps::is_dirty(*dep) { deps::add_dep(*dep); @@ -54,7 +53,7 @@ impl<K: Clone + Eq + Hash + Send + Sync, V: Send + Sync, C: Thunk<K, V>> Mapper reuse_dep_id = Some(*dep); drop(finished); // Dirty, so we'll recompute below but we should remove it now - if self.calculated.write().remove(key).is_none() { + 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") } @@ -62,19 +61,22 @@ impl<K: Clone + Eq + Hash + Send + Sync, V: Send + Sync, C: Thunk<K, V>> Mapper } } - let barrier = self.waiting.lock().get(key).cloned(); + 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(), |hm| hm.get(key).unwrap()); + let val = RwLockReadGuard::map(self.calculated.read().await, |hm| hm.get(key).unwrap()); deps::add_dep(val.1); - return MappedRwLockReadGuard::map(val, |inf| &inf.0); + return RwLockReadGuard::map(val, |inf| &inf.0); } else { // Needs calculated let notify = Arc::new(Notify::new()); - self.waiting.lock().insert(key.clone(), notify.clone()); + self.waiting + .lock() + .await + .insert(key.clone(), notify.clone()); let dep = if let Some(x) = reuse_dep_id { deps::clear(x); @@ -86,12 +88,17 @@ impl<K: Clone + Eq + Hash + Send + Sync, V: Send + Sync, C: Thunk<K, V>> Mapper let val = deps::with_node_id(dep, self.thunk.compute(key.clone())).await; deps::add_dep(dep); - self.calculated.write().insert(key.clone(), (val, dep)); - self.waiting.lock().remove(key); + self.calculated + .write() + .await + .insert(key.clone(), (val, dep)); + self.waiting.lock().await.remove(key); notify.notify_waiters(); - return RwLockReadGuard::map(self.calculated.read(), |hm| &hm.get(key).unwrap().0); + return RwLockReadGuard::map(self.calculated.read().await, |hm| { + &hm.get(key).unwrap().0 + }); } } } |