Make it impossible for background threads to mutate Salsa inputs#1314
Make it impossible for background threads to mutate Salsa inputs#1314lionel- wants to merge 1 commit into
Conversation
DavisVaughan
left a comment
There was a problem hiding this comment.
I really really like the idea a lot, but the naming is throwing me off rn
| /// The main loop owns and mutates this. Background readers get a | ||
| /// [`WorldSnapshot`] instead, which holds a read-only [`Analysis`] in place of | ||
| /// the writable `OakDatabase`. This prevents background threads from reaching a | ||
| /// Salsa input setter. See [`Self::diagnostics_snapshot`] and | ||
| /// [`Self::snapshot`]. This split mirrors rust-analyzer's `GlobalState` and | ||
| /// `GlobalStateSnapshot`. |
There was a problem hiding this comment.
| /// The main loop owns and mutates this. Background readers get a | |
| /// [`WorldSnapshot`] instead, which holds a read-only [`Analysis`] in place of | |
| /// the writable `OakDatabase`. This prevents background threads from reaching a | |
| /// Salsa input setter. See [`Self::diagnostics_snapshot`] and | |
| /// [`Self::snapshot`]. This split mirrors rust-analyzer's `GlobalState` and | |
| /// `GlobalStateSnapshot`. | |
| /// The main loop owns and mutates this. Background readers get a | |
| /// [`WorldStateSnapshot`] instead, which holds a read-only [`OakDatabaseSnapshot`] in place of | |
| /// the writable `OakDatabase`. This prevents background threads from reaching a | |
| /// Salsa input setter. See [`Self::diagnostics_snapshot`] and | |
| /// [`Self::snapshot`]. This split mirrors rust-analyzer's `GlobalState` and | |
| /// `GlobalStateSnapshot`. |
I like this idea a lot, but I find the names to be very confusing in the long term.
Like this used to make sense
state: WorldState,but no longer does to me as
state: WorldSnapshot,I would prefer
state: WorldStateSnapshot,And Analysis doesn't really mean much to me at all, I would definitely like to see that tied to the OakDatabase name in some way. I'd be fine with OakDatabaseSnapshot or ReadOnlyOakDatabase.
Like seeing pub(crate) db: Analysis, just confuses me naming wise :/
| /// Read-only handle to an `OakDatabase` that acts as a read-only snapshot. | ||
| /// | ||
| /// An `Analysis` snapshot holds the DB clone in a private field and lends a | ||
| /// shared ref via `read()`. This prevents a read-only background workers | ||
| /// from reaching DB setters and deadock with the main loop. This is oak's | ||
| /// version of rust-analyzer's `Analysis` facade. | ||
| #[derive(Clone, Debug)] | ||
| pub(crate) struct Analysis { | ||
| db: OakDatabase, | ||
| } |
There was a problem hiding this comment.
I have a gut feeling this is too complex for us
What if we just had
pub(crate) struct WorldStateSnapshot {
// Private field. We only give out `&dyn ArkDb` handles on threads to enforce that it is read-only.
db: OakDatabase
}
impl WorldStateSnapshot {
/// Read-only access to the `OakDatabase`. Important that we return as `&dyn ArkDb`
/// to prevent the thread from simply `.clone()`ing to get its own copy that it can call
/// setters with.
pub(crate) db(&self) -> &dyn ArkDb {
&self.db
}
}Your little test only cancellation_token() could live on WorldStateSnapshot, I think.
This setup makes a lot of sense to me! And avoids a lot of the layers that seem to make naming this thing hard.
Part of #1212
This approach is taken from Rust-Analyzer where a snapshot is guaranteed to never write to the DB. Having this type-enforced safety is nice because writing to the DB from outside the main loop could deadlock.
New
WorldSnapshottype that's the read-only counterpart toWorldState. It holds a clone of the Salsa data wrapped inAnalysis, as well as clones of other world state fields.New
Analysistype that holds theOakDatabasein a private field, and lends it only as a shared reference. This is what prevents writes.