aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--incria/Cargo.lock233
-rw-r--r--incria/Cargo.toml8
-rw-r--r--incria/README.md9
-rw-r--r--incria/examples/spreadsheet.rs28
-rw-r--r--incria/src/deps.rs12
-rw-r--r--incria/src/lib.rs5
-rw-r--r--incria/src/thunk.rs31
7 files changed, 69 insertions, 257 deletions
diff --git a/incria/Cargo.lock b/incria/Cargo.lock
index d815c99..fa39449 100644
--- a/incria/Cargo.lock
+++ b/incria/Cargo.lock
@@ -9,24 +9,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "bytes"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -36,11 +18,10 @@ dependencies = [
]
[[package]]
-name = "inc"
+name = "incria"
version = "0.1.0"
dependencies = [
"once_cell",
- "parking_lot",
"tokio",
]
@@ -51,37 +32,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
-name = "lock_api"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
-[[package]]
-name = "log"
-version = "0.4.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "mio"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
-dependencies = [
- "libc",
- "log",
- "wasi",
- "windows-sys 0.45.0",
-]
-
-[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -98,29 +48,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
-name = "parking_lot"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-sys 0.45.0",
-]
-
-[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -145,46 +72,6 @@ dependencies = [
]
[[package]]
-name = "redox_syscall"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
-
-[[package]]
-name = "socket2"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
name = "syn"
version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -202,16 +89,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105"
dependencies = [
"autocfg",
- "bytes",
- "libc",
- "mio",
"num_cpus",
- "parking_lot",
"pin-project-lite",
- "signal-hook-registry",
- "socket2",
"tokio-macros",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -232,64 +113,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
-[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets 0.48.0",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets",
]
[[package]]
@@ -298,95 +127,53 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
-[[package]]
-name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
-[[package]]
-name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
-[[package]]
-name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
-[[package]]
-name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-
-[[package]]
-name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/incria/Cargo.toml b/incria/Cargo.toml
index 64fe414..1ff3824 100644
--- a/incria/Cargo.toml
+++ b/incria/Cargo.toml
@@ -1,9 +1,11 @@
[package]
-name = "inc"
+name = "incria"
version = "0.1.0"
edition = "2021"
[dependencies]
once_cell = "1.17.1"
-parking_lot = "0.12.1"
-tokio = { version = "1.28.1", features = ["full"] }
+tokio = { version = "1.28.1", features = ["sync", "rt"] }
+
+[dev-dependencies]
+tokio = { version = "1.28.1", features = ["macros", "sync", "rt-multi-thread"] }
diff --git a/incria/README.md b/incria/README.md
new file mode 100644
index 0000000..01c8438
--- /dev/null
+++ b/incria/README.md
@@ -0,0 +1,9 @@
+# incria
+
+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.
+
+For more info, read the rustdoc or check the [/examples](examples).
diff --git a/incria/examples/spreadsheet.rs b/incria/examples/spreadsheet.rs
index 40b1d5c..135ca80 100644
--- a/incria/examples/spreadsheet.rs
+++ b/incria/examples/spreadsheet.rs
@@ -1,6 +1,6 @@
use std::{future::Future, pin::Pin};
-use inc::{
+use incria::{
deps,
thunk::{Thunk, ThunkMapper},
Mapper,
@@ -11,41 +11,41 @@ type Key = (usize, usize);
type Value = usize;
#[derive(Debug, Default)]
-struct CellComputer;
+struct CellThunk;
-impl Thunk<Key, Value> for CellComputer {
+impl Thunk<Key, Value> for CellThunk {
fn compute(&self, key: Key) -> Pin<Box<dyn Future<Output = Value> + Send + '_>> {
Box::pin(async move {
if key.0 > 0 {
- let prev1 = *cell_store().get(&(key.0 - 1, key.1 - 1)).await;
- let prev2 = *cell_store().get(&(key.0 - 1, key.1)).await;
- let prev3 = *cell_store().get(&(key.0 - 1, key.1 + 1)).await;
+ let prev1 = *cell_mapping().get(&(key.0 - 1, key.1 - 1)).await;
+ let prev2 = *cell_mapping().get(&(key.0 - 1, key.1)).await;
+ let prev3 = *cell_mapping().get(&(key.0 - 1, key.1 + 1)).await;
prev1 + prev2 + prev3
} else {
- key.1
+ 1
}
})
}
}
-type CellStore = ThunkMapper<Key, Value, CellComputer>;
-static CELL_STORE: OnceCell<CellStore> = OnceCell::new();
-fn cell_store() -> &'static CellStore {
- CELL_STORE.get_or_init(CellStore::default)
+type CellMapping = ThunkMapper<Key, Value, CellThunk>;
+static CELL_MAPPING: OnceCell<CellMapping> = OnceCell::new();
+fn cell_mapping() -> &'static CellMapping {
+ CELL_MAPPING.get_or_init(CellMapping::default)
}
-const N: usize = 5;
+const N: usize = 20;
#[tokio::main]
async fn main() {
deps::with_node_id(deps::next_node_id(), async {
- let val = *dbg!(cell_store().get(&(N, N)).await);
+ let val = *dbg!(cell_mapping().get(&(N, N)).await);
// println!("{}", dep_graphviz());
deps::mark_dirty(21);
// println!("{}", dep_graphviz());
- assert_eq!(val, *cell_store().get(&(N, N)).await);
+ assert_eq!(val, *cell_mapping().get(&(N, N)).await);
println!("{}", deps::dep_graphviz());
})
.await;
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
+ });
}
}
}