mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-22 12:56:33 +00:00
Update book
This commit is contained in:
parent
5ba40cf36c
commit
b3838b1ca7
11 changed files with 46 additions and 94 deletions
|
@ -1,2 +0,0 @@
|
|||
# Redirects from what the browser requests to what we serve
|
||||
/ /salsa2022
|
|
@ -14,33 +14,4 @@ curl -L https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/download/v$
|
|||
unzip mdbook-linkcheck.v$MDBOOK_LINKCHECK_VERSION.x86_64-unknown-linux-gnu.zip -d ~/.cargo/bin
|
||||
chmod +x ~/.cargo/bin/mdbook-linkcheck
|
||||
|
||||
# ======================================================================
|
||||
# The following script automates the deployment of both the latest and a
|
||||
# specified older version of the 'salsa' documentation using mdbook
|
||||
|
||||
# Store the current branch or commit
|
||||
original_branch=$(git rev-parse --abbrev-ref HEAD)
|
||||
if [ "$original_branch" == "HEAD" ]; then
|
||||
original_branch=$(git rev-parse HEAD)
|
||||
fi
|
||||
|
||||
mkdir -p versions # Create a root directory for all versions
|
||||
|
||||
# Declare an associative array to map commits to custom version directory names
|
||||
declare -A commit_to_version=( ["$original_branch"]="salsa2022" ["754eea8b5f8a31b1100ba313d59e41260b494225"]="salsa" )
|
||||
|
||||
# Loop over the keys (commit hashes or branch names) in the associative array
|
||||
for commit in "${!commit_to_version[@]}"; do
|
||||
git checkout $commit
|
||||
mdbook build
|
||||
version_dir="versions/${commit_to_version[$commit]}"
|
||||
mkdir -p $version_dir
|
||||
mv book/html/* $version_dir
|
||||
rm -rf book
|
||||
done
|
||||
|
||||
# Return to the original branch or commit
|
||||
git checkout $original_branch
|
||||
|
||||
# Copy _redirects to the root directory
|
||||
cp _redirects versions
|
||||
mdbook build
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
> ⚠️ **IN-PROGRESS VERSION OF SALSA.** ⚠️
|
||||
>
|
||||
> This page describes the unreleased "Salsa 2022" version, which is a major departure from older versions of salsa. The code here works but is only available on github and from the `salsa-2022` crate.
|
||||
>
|
||||
> If you are looking for the older version of salsa, simply visit [this link](https://salsa-rs.netlify.app/salsa)
|
|
@ -1,7 +1,5 @@
|
|||
# On-Demand (Lazy) Inputs
|
||||
|
||||
{{#include ../caveat.md}}
|
||||
|
||||
Salsa inputs work best if you can easily provide all of the inputs upfront.
|
||||
However sometimes the set of inputs is not known beforehand.
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# Salsa overview
|
||||
|
||||
{{#include caveat.md}}
|
||||
|
||||
This page contains a brief overview of the pieces of a Salsa program.
|
||||
For a more detailed look, check out the [tutorial](./tutorial.md), which walks through the creation of an entire project end-to-end.
|
||||
|
||||
|
@ -151,7 +149,7 @@ Tracked functions can return any clone-able type. A clone is required since, whe
|
|||
**Tracked structs** are intermediate structs created during your computation.
|
||||
Like inputs, their fields are stored inside the database, and the struct itself just wraps an id.
|
||||
Unlike inputs, they can only be created inside a tracked function, and their fields can never change once they are created (until the next revision, at least).
|
||||
Getter methods are provided to read the fields, but there are no setter methods.
|
||||
Getter methods are provided to read the fields, but there are no setter methods.
|
||||
Example:
|
||||
|
||||
```rust
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# Plumbing
|
||||
|
||||
{{#include caveat.md}}
|
||||
|
||||
This chapter documents the code that salsa generates and its "inner workings".
|
||||
We refer to this as the "plumbing".
|
||||
|
||||
|
@ -9,11 +7,11 @@ 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) 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.
|
||||
* The [derived queries flowchart](./plumbing/derived_flowchart.md) depicts the logic in flowchart form.
|
||||
* The [cycle handling](./plumbing/cycles.md) handling chapter describes what happens when cycles occur.
|
||||
* The [terminology](./plumbing/terminology.md) section describes various words that appear throughout.
|
||||
- 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) 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.
|
||||
- The [derived queries flowchart](./plumbing/derived_flowchart.md) depicts the logic in flowchart form.
|
||||
- The [cycle handling](./plumbing/cycles.md) handling chapter describes what happens when cycles occur.
|
||||
- The [terminology](./plumbing/terminology.md) section describes various words that appear throughout.
|
||||
|
|
|
@ -13,8 +13,8 @@ struct MyDatabase {
|
|||
|
||||
This data is divided into two categories:
|
||||
|
||||
* Salsa-governed storage, contained in the `Storage<Self>` field. This data is mandatory.
|
||||
* Other fields (like `maybe_other_fields`) defined by the user. This can be anything. This allows for you to give access to special resources or whatever.
|
||||
- Salsa-governed storage, contained in the `Storage<Self>` field. This data is mandatory.
|
||||
- Other fields (like `maybe_other_fields`) defined by the user. This can be anything. This allows for you to give access to special resources or whatever.
|
||||
|
||||
## Parallel handles
|
||||
|
||||
|
@ -28,10 +28,10 @@ The `Snapshot` method returns a `Snapshot<DB>` type, which prevents these clones
|
|||
The salsa `Storage` struct contains all the data that salsa itself will use and work with.
|
||||
There are three key bits of data:
|
||||
|
||||
* The `Shared` struct, which contains the data stored across all snapshots. This is primarily the ingredients described in the [jars and ingredients chapter](./jars_and_ingredients.md), but it also contains some synchronization information (a cond var). This is used for cancellation, as described below.
|
||||
* The data in the `Shared` struct is only shared across threads when other threads are active. Some operations, like mutating an input, require an `&mut` handle to the `Shared` struct. This is obtained by using the `Arc::get_mut` methods; obviously this is only possible when all snapshots and threads have ceased executing, since there must be a single handle to the `Arc`.
|
||||
* 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 shared data of its own.
|
||||
- The `Shared` struct, which contains the data stored across all snapshots. This is primarily the ingredients described in the [jars and ingredients chapter](./jars_and_ingredients.md), but it also contains some synchronization information (a cond var). This is used for cancellation, as described below.
|
||||
- The data in the `Shared` struct is only shared across threads when other threads are active. Some operations, like mutating an input, require an `&mut` handle to the `Shared` struct. This is obtained by using the `Arc::get_mut` methods; obviously this is only possible when all snapshots and threads have ceased executing, since there must be a single handle to the `Arc`.
|
||||
- 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 shared data of its own.
|
||||
|
||||
## Incrementing the revision counter and getting mutable access to the jars
|
||||
|
||||
|
@ -43,20 +43,20 @@ Each of the snapshots however onlys another handle on the `Arc` in `Storage` tha
|
|||
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
|
||||
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}}
|
||||
{{#include ../../../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}}
|
||||
{{#include ../../../src/storage.rs:cancel_other_workers}}
|
||||
```
|
||||
|
||||
## The Salsa runtime
|
||||
|
@ -68,5 +68,3 @@ 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.
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
# Jars and ingredients
|
||||
|
||||
{{#include ../caveat.md}}
|
||||
|
||||
This page covers how data is organized in Salsa and how links between Salsa items (e.g., dependency tracking) work.
|
||||
|
||||
## Salsa items and ingredients
|
||||
## Salsa items and ingredients
|
||||
|
||||
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:
|
||||
|
@ -117,7 +115,7 @@ struct MyDatabase {
|
|||
...the `salsa::db` macro would generate a `HasJars` impl that (among other things) contains `type Jars = (Jar1, ..., JarN)`:
|
||||
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022-macros/src/db.rs:HasJars}}
|
||||
{{#include ../../../components/salsa-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.
|
||||
|
@ -131,7 +129,7 @@ This is a 32-bit number that identifies a particular ingredient from a particula
|
|||
|
||||
### Routes
|
||||
|
||||
In addition to an index, each ingredient in the database also has a corresponding *route*.
|
||||
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
|
||||
|
@ -145,7 +143,7 @@ A `DatabaseKeyIndex` identifies a specific value stored in some specific ingredi
|
|||
It combines an [`IngredientIndex`] with a `key_index`, which is a `salsa::Id`:
|
||||
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022/src/key.rs:DatabaseKeyIndex}}
|
||||
{{#include ../../../src/key.rs:DatabaseKeyIndex}}
|
||||
```
|
||||
|
||||
A `DependencyIndex` is similar, but the `key_index` is optional.
|
||||
|
@ -153,11 +151,11 @@ This is used when we sometimes wish to refer to the ingredient as a whole, and n
|
|||
|
||||
These kinds of indices are used to store connetions between ingredients.
|
||||
For example, each memoized value has to track its inputs.
|
||||
Those inputs are stored as dependency indices.
|
||||
Those inputs are stored as dependency indices.
|
||||
We can then do things like ask, "did this input change since revision R?" by
|
||||
|
||||
* using the ingredient index to find the route and get a `&dyn Ingredient<DB>`
|
||||
* and then invoking the `maybe_changed_since` method on that trait object.
|
||||
- using the ingredient index to find the route and get a `&dyn Ingredient<DB>`
|
||||
- and then invoking the `maybe_changed_since` method on that trait object.
|
||||
|
||||
### `HasJarsDyn`
|
||||
|
||||
|
@ -166,23 +164,23 @@ The user's code always interacts with a `dyn crate::Db` value, where `crate::Db`
|
|||
Ideally, we would have `salsa::Database` extend `salsa::HasJars`, which is the main trait that gives access to the jars data.
|
||||
But we don't want to do that because `HasJars` defines an associated type `Jars`, and that would mean that every reference to `dyn crate::Db` would have to specify the jars type using something like `dyn crate::Db<Jars = J>`.
|
||||
This would be unergonomic, but what's worse, it would actually be impossible: the final Jars type combines the jars from multiple crates, and so it is not known to any individual jar crate.
|
||||
To workaround this, `salsa::Database` in fact extends *another* trait, `HasJarsDyn`, that doesn't reveal the `Jars` or ingredient types directly, but just has various method that can be performed on an ingredient, given its `IngredientIndex`.
|
||||
To workaround this, `salsa::Database` in fact extends _another_ trait, `HasJarsDyn`, that doesn't reveal the `Jars` or ingredient types directly, but just has various method that can be performed on an ingredient, given its `IngredientIndex`.
|
||||
Traits like `Ingredient<DB>` require knowing the full `DB` type.
|
||||
If we had one function ingredient directly invoke a method on `Ingredient<DB>`, that would imply that it has to be fully generic and only instantiated at the final crate, when the full database type is available.
|
||||
|
||||
We solve this via the `HasJarsDyn` trait. The `HasJarsDyn` trait exports a method that combines the "find ingredient, invoking method" steps into one method:
|
||||
|
||||
```rust,ignore aasaaasdfijjAasdfa
|
||||
{{#include ../../../components/salsa-2022/src/storage.rs:HasJarsDyn}}
|
||||
{{#include ../../../src/storage.rs:HasJarsDyn}}
|
||||
```
|
||||
|
||||
So, technically, to check if an input has changed, an ingredient:
|
||||
|
||||
* Invokes `HasJarsDyn::maybe_changed_after` on the `dyn Database`
|
||||
* The impl for this method (generated by `#[salsa::db]`):
|
||||
* gets the route for the ingredient from the ingredient index
|
||||
* uses the route to get a `&dyn Ingredient`
|
||||
* invokes `maybe_changed_after` on that ingredient
|
||||
- Invokes `HasJarsDyn::maybe_changed_after` on the `dyn Database`
|
||||
- The impl for this method (generated by `#[salsa::db]`):
|
||||
- gets the route for the ingredient from the ingredient index
|
||||
- uses the route to get a `&dyn Ingredient`
|
||||
- invokes `maybe_changed_after` on that ingredient
|
||||
|
||||
### Initializing the database
|
||||
|
||||
|
@ -190,7 +188,7 @@ The last thing to dicsuss is how the database is initialized.
|
|||
The `Default` implementation for `Storage<DB>` does the work:
|
||||
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022/src/storage.rs:default}}
|
||||
{{#include ../../../src/storage.rs:default}}
|
||||
```
|
||||
|
||||
First, it creates an empty `Routes` instance.
|
||||
|
@ -198,16 +196,16 @@ Then it invokes the `DB::create_jars` method.
|
|||
The implementation of this method is defined by the `#[salsa::db]` macro; it invokes `salsa::plumbing::create_jars_inplace` to allocate memory for the jars, and then invokes the `Jar::init_jar` method on each of the jars to initialize them:
|
||||
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022-macros/src/db.rs:create_jars}}
|
||||
{{#include ../../../components/salsa-macros/src/db.rs:create_jars}}
|
||||
```
|
||||
|
||||
This implementation for `init_jar` is generated by the `#[salsa::jar]` macro, and simply walks over the representative type for each salsa item and asks *it* to create its ingredients
|
||||
This implementation for `init_jar` is generated by the `#[salsa::jar]` macro, and simply walks over the representative type for each salsa item and asks _it_ to create its ingredients
|
||||
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022-macros/src/jar.rs:init_jar}}
|
||||
{{#include ../../../components/salsa-macros/src/jar.rs:init_jar}}
|
||||
```
|
||||
|
||||
The code to create the ingredients for any particular item is generated by their associated macros (e.g., `#[salsa::tracked]`, `#[salsa::input]`), but it always follows a particular structure.
|
||||
To create an ingredient, we first invoke `Routes::push`, which creates the routes to that ingredient and assigns it an `IngredientIndex`.
|
||||
We can then invoke a function such as `FunctionIngredient::new` to create the structure.
|
||||
The *routes* to an ingredient are defined as closures that, given the `DB::Jars`, can find the data for a particular ingredient.
|
||||
The _routes_ to an ingredient are defined as closures that, given the `DB::Jars`, can find the data for a particular ingredient.
|
||||
|
|
|
@ -20,11 +20,11 @@ contains both the field values but also the revisions when they last changed val
|
|||
|
||||
## Each tracked struct has a globally unique id
|
||||
|
||||
This will begin by creating a *globally unique, 32-bit id* for the tracked struct. It is created by interning a combination of
|
||||
This will begin by creating a _globally unique, 32-bit id_ for the tracked struct. It is created by interning a combination of
|
||||
|
||||
* the currently executing query;
|
||||
* a u64 hash of the `#[id]` fields;
|
||||
* a *disambiguator* that makes this hash unique within the current query. i.e., when a query starts executing, it creates an empty map, and the first time a tracked struct with a given hash is created, it gets disambiguator 0. The next one will be given 1, etc.
|
||||
- the currently executing query;
|
||||
- a u64 hash of the `#[id]` fields;
|
||||
- a _disambiguator_ that makes this hash unique within the current query. i.e., when a query starts executing, it creates an empty map, and the first time a tracked struct with a given hash is created, it gets disambiguator 0. The next one will be given 1, etc.
|
||||
|
||||
## Each tracked struct has a `ValueStruct` storing its data
|
||||
|
||||
|
@ -32,10 +32,10 @@ The struct and field ingredients share access to a hashmap that maps
|
|||
each field id to a value struct:
|
||||
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022/src/tracked_struct.rs:ValueStruct}}
|
||||
{{#include ../../../src/tracked_struct.rs:ValueStruct}}
|
||||
```
|
||||
|
||||
The value struct stores the values of the fields but also the revisions when
|
||||
The value struct stores the values of the fields but also the revisions when
|
||||
that field last changed. Each time the struct is recreated in a new revision,
|
||||
the old and new values for its fields are compared and a new revision is created.
|
||||
|
||||
|
@ -46,5 +46,5 @@ but also various important operations such as extracting the hashable id fields
|
|||
and updating the "revisions" to track when a field last changed:
|
||||
|
||||
```rust,ignore
|
||||
{{#include ../../../components/salsa-2022/src/tracked_struct.rs:Configuration}}
|
||||
{{#include ../../../src/tracked_struct.rs:Configuration}}
|
||||
```
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# Tutorial: calc
|
||||
|
||||
{{#include caveat.md}}
|
||||
|
||||
This tutorial walks through an end-to-end example of using Salsa.
|
||||
It does not assume you know anything about salsa,
|
||||
but reading the [overview](./overview.md) first is probably a good idea to get familiar with the basic concepts.
|
||||
|
|
|
@ -11,9 +11,9 @@ description = "Procedural macros for the salsa crate"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
eyre = "0.6.5"
|
||||
heck = "0.4"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
eyre = "0.6.5"
|
||||
syn = { version = "2.0.64", features = ["full", "visit-mut"] }
|
||||
synstructure = "0.13.1"
|
||||
|
|
Loading…
Reference in a new issue