mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-02-02 09:46:06 +00:00
start documenting plumbing
This commit is contained in:
parent
becaade31e
commit
5b9da4a9c0
1 changed files with 151 additions and 3 deletions
|
@ -1,9 +1,157 @@
|
|||
# Plumbing
|
||||
|
||||
{{#include caveat.md}}
|
||||
|
||||
This chapter documents the code that salsa generates and its "inner workings".
|
||||
We refer to this as the "plumbing".
|
||||
|
||||
## History
|
||||
## Salsa items and ingredients
|
||||
|
||||
* 2020-07-05: Updated to take [RFC 6](rfcs/RFC0006-Dynamic-Databases.md) into account.
|
||||
* 2020-06-24: Initial version.
|
||||
A **salsa item** is some item annotated with a salsa annotation that can be included in a jar.
|
||||
For example, a tracked function is a salsa item:
|
||||
|
||||
```rust
|
||||
#[salsa::tracked]
|
||||
fn foo(db: &dyn Db, input: MyInput) { }
|
||||
```
|
||||
|
||||
...and so is a salsa input...
|
||||
|
||||
```rust
|
||||
#[salsa::input]
|
||||
struct MyInput { }
|
||||
```
|
||||
|
||||
...or a tracked struct:
|
||||
|
||||
```rust
|
||||
#[salsa::tracked]
|
||||
struct MyStruct { }
|
||||
```
|
||||
|
||||
Each salsa item needs certain bits of data at runtime to operate.
|
||||
These bits of data are called **ingredients**.
|
||||
Most salsa items generate a single ingredient, but sometimes they make more than one.
|
||||
For example, a tracked function generates a [`FunctionIngredient`].
|
||||
A tracked struct however generates several ingredients, one for the struct itself (a [`TrackedStructIngredient`],
|
||||
and one [`FunctionIngredient`] for each value field.
|
||||
|
||||
[`FunctionIngredient`]: https://github.com/salsa-rs/salsa/blob/becaade31e6ebc58cd0505fc1ee4b8df1f39f7de/components/salsa-2022/src/function.rs#L42
|
||||
[`TrackedStructIngredient`]: https://github.com/salsa-rs/salsa/blob/becaade31e6ebc58cd0505fc1ee4b8df1f39f7de/components/salsa-2022/src/tracked_struct.rs#L18
|
||||
|
||||
### Ingredients define the core logic of salsa
|
||||
|
||||
Most of the interesting salsa code lives in these ingredients.
|
||||
For example, when you create a new tracked struct, the method [`TrackedStruct::new_struct`] is invoked;
|
||||
it is responsible for determining the tracked struct's id.
|
||||
Similarly, when you call a tracked function, that is translated into a call to [`TrackedFunction::fetch`],
|
||||
which decides whether there is a valid memoized value to return,
|
||||
or whether the function must be executed.
|
||||
|
||||
[`TrackedStruct::new_struct`]: https://github.com/salsa-rs/salsa/blob/becaade31e6ebc58cd0505fc1ee4b8df1f39f7de/components/salsa-2022/src/tracked_struct.rs#L76
|
||||
[`TrackedFunction::fetch`]: https://github.com/salsa-rs/salsa/blob/becaade31e6ebc58cd0505fc1ee4b8df1f39f7de/components/salsa-2022/src/function/fetch.rs#L15
|
||||
|
||||
### Ingredient interfaces are not stable or subject to semver
|
||||
|
||||
Interfaces are not meant to be directly used by salsa users.
|
||||
The salsa macros generate code that invokes the ingredients.
|
||||
The APIs may change in arbitrary ways across salsa versions,
|
||||
as the macros are kept in sync.
|
||||
|
||||
### The `Ingredient` trait
|
||||
|
||||
Each ingredient implements the [`Ingredient<DB>`] trait, which defines generic operations supported by any kind of ingredient.
|
||||
For example, the method `maybe_changed_after` can be used to check whether some particular piece of data stored in the ingredient may have changed since a given revision:
|
||||
|
||||
[`Ingredient<DB>`]: https://github.com/salsa-rs/salsa/blob/becaade31e6ebc58cd0505fc1ee4b8df1f39f7de/components/salsa-2022/src/ingredient.rs#L15
|
||||
[`maybe_changed_after`]: https://github.com/salsa-rs/salsa/blob/becaade31e6ebc58cd0505fc1ee4b8df1f39f7de/components/salsa-2022/src/ingredient.rs#L21-L22
|
||||
|
||||
We'll see below that each database `DB` is able to take an `IngredientIndex` and use that to get a `&dyn Ingredient<DB>` for the corresponding ingredient.
|
||||
This allows the database to perform generic operations on a numbered ingredient without knowing exactly what the type of that ingredient is.
|
||||
|
||||
### Jars are a collection of ingredients
|
||||
|
||||
When you declare a salsa jar, you list out each of the salsa items that are included in that jar:
|
||||
|
||||
```rust
|
||||
#[salsa::jar]
|
||||
struct Jar(
|
||||
foo,
|
||||
MyInput,
|
||||
MyStruct
|
||||
);
|
||||
```
|
||||
|
||||
This expands to a struct like so:
|
||||
|
||||
```rust
|
||||
struct Jar(
|
||||
<foo as IngredientsFor>::Ingredient,
|
||||
<MyInput as IngredientsFor>::Ingredient,
|
||||
<MyStruct as IngredientsFor>::Ingredient,
|
||||
)
|
||||
```
|
||||
|
||||
The `IngredientsFor` trait is used to define the ingredients needed by some salsa item, such as the tracked function `foo`
|
||||
or the tracked struct `MyInput`.
|
||||
Each salsa item defines a type `I`, so that `<I as IngredientsFor>::Ingredient` gives the ingredients needed by `I`.
|
||||
|
||||
### Database is a tuple of jars
|
||||
|
||||
Salsa's database storage ultimately boils down to a tuple of jar structs,
|
||||
where each jar struct (as we just saw) itself contains the ingredients
|
||||
for the salsa items within that jar.
|
||||
The database can thus be thought of as a list of ingredients,
|
||||
although that list is organized into a 2-level hierarchy.
|
||||
|
||||
The reason for this 2-level hierarchy is that it permits separate compilation and privacy.
|
||||
The crate that lists the jars doens't have to know the contents of the jar to embed the jar struct in the database.
|
||||
And some of the types that appear in the jar may be private to another struct.
|
||||
|
||||
### The HasJars trait and the Jars type
|
||||
|
||||
Each salsa database implements the `HasJars` trait,
|
||||
generated by the `salsa::db` procedural macro.
|
||||
The `HarJars` trait, among other things, defines a `Jars` associated type that maps to a tuple of the jars in the trait.
|
||||
|
||||
For example, given a database like this...
|
||||
|
||||
```rust
|
||||
#[salsa::db(Jar1, ..., JarN)]
|
||||
struct MyDatabase {
|
||||
storage: salsa::Storage<Self>
|
||||
}
|
||||
```
|
||||
|
||||
...the `salsa::db` macro would generate a `HasJars` impl that (among other things) contains...
|
||||
|
||||
```rust
|
||||
impl HarJars for MyDatabase {
|
||||
type Jars = (Jar1, ..., JarN);
|
||||
|
||||
/* other stuff elided */
|
||||
}
|
||||
```
|
||||
|
||||
In turn, the `salsa::Storage<DB>` type ultimately contains a struct `Shared` that embeds `DB::Jars`, thus embedding all the data for each jar.
|
||||
|
||||
### Ingredient indices
|
||||
|
||||
During initialization, each ingredient in the database is assigned a unique index called the [`IngredientIndex`].
|
||||
This is a 32-bit number that identifies a particular ingredient from a particular jar.
|
||||
|
||||
[`IngredientIndex`]: https://github.com/salsa-rs/salsa/blob/becaade31e6ebc58cd0505fc1ee4b8df1f39f7de/components/salsa-2022/src/routes.rs#L5-L9
|
||||
|
||||
### Routes
|
||||
|
||||
In addition to an index, each ingredient in the database also has a corresponding *route*.
|
||||
A route is a closure that, given a reference to the `DB::Jars` tuple,
|
||||
returns a `&dyn Ingredient<DB>` reference.
|
||||
The route table allows us to go from the `IngredientIndex` for a particular ingredient
|
||||
to its `&dyn Ingredient<DB>` trait object.
|
||||
The route table is created while the database is being initialized,
|
||||
as described shortly.
|
||||
|
||||
### Database keys and dependency keys
|
||||
|
||||
### Initializing the database
|
||||
|
|
Loading…
Reference in a new issue