aboutsummaryrefslogtreecommitdiff
path: root/incria/src
diff options
context:
space:
mode:
authortcmal <tcmal>2023-05-24 19:13:34 +0000
committerAria <me@aria.rip>2023-10-01 17:31:29 +0100
commit2c78bb68b9ff51cfb24ce1b7e111339ab5b37746 (patch)
tree6d33270f3bbf561343d9a300bc3dcede4518f115 /incria/src
parentf31fcefd85cb69f0c7aa8922f3a579a86eac415d (diff)
update name and write readme
Diffstat (limited to 'incria/src')
-rw-r--r--incria/src/deps.rs12
-rw-r--r--incria/src/lib.rs5
-rw-r--r--incria/src/thunk.rs31
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
+ });
}
}
}