Update book

This commit is contained in:
Matthijs Brobbel 2024-06-18 09:59:17 +02:00
parent 5ba40cf36c
commit b3838b1ca7
No known key found for this signature in database
GPG key ID: 551189F8515034D3
11 changed files with 46 additions and 94 deletions

View file

@ -1,2 +0,0 @@
# Redirects from what the browser requests to what we serve
/ /salsa2022

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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}}
```

View file

@ -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.

View file

@ -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"