mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-02-12 06:46:28 +00:00
explain more about rev counter, include snippets
This commit is contained in:
parent
030caa1d21
commit
06e0a04cb3
6 changed files with 62 additions and 27 deletions
|
@ -10,7 +10,7 @@ We refer to this as the "plumbing".
|
|||
The plumbing section is broken up into chapters:
|
||||
|
||||
* The [jars and ingredients](./plumbing/jars_and_ingredients.md) covers how each salsa item (like a tracked function) specifies what data it needs and runtime, and how links between items work.
|
||||
* The [database and runtime](./plumbing/database_and_runtime.md)
|
||||
* The [database and runtime](./plumbing/database_and_runtime.md) covers the data structures that are used at runtime to coordinate workers, trigger cancellation, track which functions are active and what dependencies they have accrued, and so forth.
|
||||
* The [query operations](./plumbing/query_ops.md) chapter describes how the major operations on function ingredients work. This text was written for an older version of salsa but the logic is the same:
|
||||
* The [maybe changed after](./plumbing/maybe_changed_after.md) operation determines when a memoized value for a tracked function is out of date.
|
||||
* The [fetch](./plumbing/fetch.md) operation computes the most recent value.
|
||||
|
|
|
@ -33,6 +33,32 @@ There are three key bits of data:
|
|||
* The `Routes` struct, which contains the information to find any particular ingredient -- this is also shared across all handles, and its construction is also described in the [jars and ingredients chapter](./jars_and_ingredients.md). The routes are separated out from the `Shared` struct because they are truly immutable at all times, and we want to be able to hold a handle to them while getting `&mut` access to the `Shared` struct.
|
||||
* The `Runtime` struct, which is specific to a particular database instance. It contains the data for a single active thread, along with some links to shraed data of its own.
|
||||
|
||||
## Incrementing the revision counter and getting mutable access to the jars
|
||||
|
||||
Salsa's general model is that there is a single "master" copy of the database and, potentially, multiple snapshots.
|
||||
The snapshots are not directly owned, they are instead enclosed in a `Snapshot<DB>` type that permits only `&`-deref,
|
||||
and so the only database that can be accessed with an `&mut`-ref is the master database.
|
||||
Each of the snapshots however onlys another handle on the `Arc` in `Storage` that stores the ingredients.
|
||||
|
||||
Whenever the user attempts to do an `&mut`-operation, such as modifying an input field, that needs to
|
||||
first cancel any parallel snapshots and wait for those parallel threads to finish.
|
||||
Once the snapshots have completed, we can use `Arc::get_mut` to get an `&mut` reference to the ingredient data.
|
||||
This allows us to get `&mut` access without any unsafe code and
|
||||
guarantees that we have successfully managed to cancel the other worker threads
|
||||
(or gotten ourselves into a deadlock).
|
||||
|
||||
The code to acquire `&mut` access to the database is the `jars_mut` method:
|
||||
|
||||
```rust
|
||||
{{#include ../../../components/salsa-2022/src/storage.rs:jars_mut}}
|
||||
```
|
||||
|
||||
The key initial point is that it invokes `cancel_other_workers` before proceeding:
|
||||
|
||||
```rust
|
||||
{{#include ../../../components/salsa-2022/src/storage.rs:cancel_other_workers}}
|
||||
```
|
||||
|
||||
## The Salsa runtime
|
||||
|
||||
The salsa runtime offers helper methods that are accessed by the ingredients.
|
||||
|
@ -41,4 +67,6 @@ It also tracks the current revision and information about when values with low o
|
|||
|
||||
Basically, the ingredient structures store the "data at rest" -- like memoized values -- and things that are "per ingredient".
|
||||
|
||||
The runtime stores the "active, in-progress" data, such as which queries are on the stack, and/or the dependencies accessed by the currently active query.
|
||||
The runtime stores the "active, in-progress" data, such as which queries are on the stack, and/or the dependencies accessed by the currently active query.
|
||||
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ This allows the database to perform generic operations on a numbered ingredient
|
|||
|
||||
When you declare a salsa jar, you list out each of the salsa items that are included in that jar:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
#[salsa::jar]
|
||||
struct Jar(
|
||||
foo,
|
||||
|
@ -115,21 +115,17 @@ The `HarJars` trait, among other things, defines a `Jars` associated type that m
|
|||
|
||||
For example, given a database like this...
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
#[salsa::db(Jar1, ..., JarN)]
|
||||
struct MyDatabase {
|
||||
storage: salsa::Storage<Self>
|
||||
}
|
||||
```
|
||||
|
||||
...the `salsa::db` macro would generate a `HasJars` impl that (among other things) contains...
|
||||
...the `salsa::db` macro would generate a `HasJars` impl that (among other things) contains `type Jars = (Jar1, ..., JarN)`:
|
||||
|
||||
```rust
|
||||
impl HarJars for MyDatabase {
|
||||
type Jars = (Jar1, ..., JarN);
|
||||
|
||||
/* other stuff elided */
|
||||
}
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022-macros/src/db.rs:HasJars}}
|
||||
```
|
||||
|
||||
In turn, the `salsa::Storage<DB>` type ultimately contains a struct `Shared` that embeds `DB::Jars`, thus embedding all the data for each jar.
|
||||
|
@ -156,11 +152,8 @@ as described shortly.
|
|||
A `DatabaseKeyIndex` identifies a specific value stored in some specific ingredient.
|
||||
It combines an [`IngredientIndex`] with a `key_index`, which is a `salsa::Id`:
|
||||
|
||||
```rust
|
||||
pub struct DatabaseKeyIndex {
|
||||
pub(crate) ingredient_index: IngredientIndex,
|
||||
pub(crate) key_index: Id,
|
||||
}
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022/src/key.rs:DatabaseKeyIndex}}
|
||||
```
|
||||
|
||||
A `DependencyIndex` is similar, but the `key_index` is optional.
|
||||
|
@ -183,15 +176,8 @@ If we had one function ingredient directly invoke a method on `Ingredient<DB>`,
|
|||
|
||||
We solve this via the `HasJarsDyn` trait. The `HasJarsDyn` trait exports method that combine the "find ingredient, invoking method" steps into one method:
|
||||
|
||||
```rust
|
||||
// Dyn friendly subset of HasJars
|
||||
pub trait HasJarsDyn {
|
||||
fn runtime(&self) -> &Runtime;
|
||||
|
||||
fn maybe_changed_after(&self, input: DependencyIndex, revision: Revision) -> bool;
|
||||
|
||||
...
|
||||
}
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022/src/storage.rs:HasJarsDyn}}
|
||||
```
|
||||
|
||||
So, technically, to check if an input has changed, an ingredient:
|
||||
|
|
|
@ -86,8 +86,10 @@ fn has_jars_impl(args: &Args, input: &syn::ItemStruct, storage: &syn::Ident) ->
|
|||
let jar_paths: Vec<&syn::Path> = args.jar_paths.iter().collect();
|
||||
let db = &input.ident;
|
||||
parse_quote! {
|
||||
// ANCHOR: HasJars
|
||||
impl salsa::storage::HasJars for #db {
|
||||
type Jars = (#(#jar_paths,)*);
|
||||
// ANCHOR_END: HasJars
|
||||
|
||||
fn jars(&self) -> (&Self::Jars, &salsa::Runtime) {
|
||||
self.#storage.jars()
|
||||
|
|
|
@ -43,6 +43,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// ANCHOR: DatabaseKeyIndex
|
||||
/// An "active" database key index represents a database key index
|
||||
/// that is actively executing. In that case, the `key_index` cannot be
|
||||
/// None.
|
||||
|
@ -51,6 +52,7 @@ pub struct DatabaseKeyIndex {
|
|||
pub(crate) ingredient_index: IngredientIndex,
|
||||
pub(crate) key_index: Id,
|
||||
}
|
||||
// ANCHOR_END: DatabaseKeyIndex
|
||||
|
||||
impl DatabaseKeyIndex {
|
||||
pub fn ingredient_index(self) -> IngredientIndex {
|
||||
|
|
|
@ -93,23 +93,35 @@ where
|
|||
&self.runtime
|
||||
}
|
||||
|
||||
// ANCHOR: jars_mut
|
||||
/// Gets mutable access to the jars. This will trigger a new revision
|
||||
/// and it will also cancel any ongoing work in the current revision.
|
||||
/// Any actual writes that occur to data in a jar should use
|
||||
/// [`Runtime::report_tracked_write`].
|
||||
pub fn jars_mut(&mut self) -> (&mut DB::Jars, &mut Runtime) {
|
||||
// Wait for all snapshots to be dropped.
|
||||
self.cancel_other_workers();
|
||||
|
||||
// Increment revision counter.
|
||||
self.runtime.new_revision();
|
||||
|
||||
let routes = self.routes.clone();
|
||||
// Acquire `&mut` access to `self.shared` -- this is only possible because
|
||||
// the snapshots have all been dropped, so we hold the only handle to the `Arc`.
|
||||
let shared = Arc::get_mut(&mut self.shared).unwrap();
|
||||
|
||||
// Inform other ingredients that a new revision has begun.
|
||||
// This gives them a chance to free resources that were being held until the next revision.
|
||||
let routes = self.routes.clone();
|
||||
for route in routes.reset_routes() {
|
||||
route(&mut shared.jars).reset_for_new_revision();
|
||||
}
|
||||
|
||||
// Return mut ref to jars + runtime.
|
||||
(&mut shared.jars, &mut self.runtime)
|
||||
}
|
||||
// ANCHOR_END: jars_mut
|
||||
|
||||
// ANCHOR: cancel_other_workers
|
||||
/// Sets cancellation flag and blocks until all other workers with access
|
||||
/// to this storage have completed.
|
||||
///
|
||||
|
@ -128,11 +140,14 @@ where
|
|||
// We create a mutex here because the cvar api requires it, but we
|
||||
// don't really need one as the data being protected is actually
|
||||
// the jars above.
|
||||
//
|
||||
// The cvar `self.shared.cvar` is notified by the `Drop` impl.
|
||||
let mutex = parking_lot::Mutex::new(());
|
||||
let mut guard = mutex.lock();
|
||||
self.shared.cvar.wait(&mut guard);
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: cancel_other_workers
|
||||
|
||||
pub fn ingredient(&self, ingredient_index: IngredientIndex) -> &dyn Ingredient<DB> {
|
||||
let route = self.routes.route(ingredient_index);
|
||||
|
@ -179,7 +194,8 @@ pub trait HasJar<J> {
|
|||
fn jar_mut(&mut self) -> (&mut J, &mut Runtime);
|
||||
}
|
||||
|
||||
// Dyn friendly subset of HasJars
|
||||
// ANCHOR: HasJarsDyn
|
||||
/// Dyn friendly subset of HasJars
|
||||
pub trait HasJarsDyn {
|
||||
fn runtime(&self) -> &Runtime;
|
||||
|
||||
|
@ -205,6 +221,7 @@ pub trait HasJarsDyn {
|
|||
/// [`SalsaStructInDb::register_dependent_fn`](`crate::salsa_struct::SalsaStructInDb::register_dependent_fn`).
|
||||
fn salsa_struct_deleted(&self, ingredient: IngredientIndex, id: Id);
|
||||
}
|
||||
// ANCHOR_END: HasJarsDyn
|
||||
|
||||
pub trait HasIngredientsFor<I>
|
||||
where
|
||||
|
|
Loading…
Reference in a new issue