mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-08-10 05:48:17 +00:00
(almost) encansulate Runtime into Zalsa
The distinction is dumb and should go away. But we still need it for a bit.
This commit is contained in:
parent
596461c213
commit
1842b1dfbb
21 changed files with 136 additions and 111 deletions
|
@ -21,6 +21,7 @@ rustc-hash = "2.0.0"
|
||||||
salsa-macro-rules = { version = "0.1.0", path = "components/salsa-macro-rules" }
|
salsa-macro-rules = { version = "0.1.0", path = "components/salsa-macro-rules" }
|
||||||
salsa-macros = { path = "components/salsa-macros" }
|
salsa-macros = { path = "components/salsa-macros" }
|
||||||
smallvec = "1.0.0"
|
smallvec = "1.0.0"
|
||||||
|
lazy_static = "1.5.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
annotate-snippets = "0.11.4"
|
annotate-snippets = "0.11.4"
|
||||||
|
|
|
@ -86,12 +86,12 @@ macro_rules! setup_input_struct {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, &mut $zalsa::Runtime) {
|
pub fn ingredient_mut(db: &mut dyn $zalsa::Database) -> (&mut $zalsa_struct::IngredientImpl<Self>, $zalsa::Revision) {
|
||||||
let zalsa_mut = db.zalsa_mut();
|
let zalsa_mut = db.zalsa_mut();
|
||||||
let index = zalsa_mut.add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default());
|
let index = zalsa_mut.add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default());
|
||||||
let (ingredient, runtime) = zalsa_mut.lookup_ingredient_mut(index);
|
let (ingredient, current_revision) = zalsa_mut.lookup_ingredient_mut(index);
|
||||||
let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl<Self>>();
|
let ingredient = ingredient.assert_type_mut::<$zalsa_struct::IngredientImpl<Self>>();
|
||||||
(ingredient, runtime)
|
(ingredient, current_revision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,8 +81,7 @@ impl<A: Accumulator> IngredientImpl<A> {
|
||||||
|
|
||||||
pub fn push(&self, db: &dyn crate::Database, value: A) {
|
pub fn push(&self, db: &dyn crate::Database, value: A) {
|
||||||
local_state::attach(db, |state| {
|
local_state::attach(db, |state| {
|
||||||
let runtime = db.zalsa().runtime();
|
let current_revision = db.zalsa().current_revision();
|
||||||
let current_revision = runtime.current_revision();
|
|
||||||
let (active_query, _) = match state.active_query() {
|
let (active_query, _) = match state.active_query() {
|
||||||
Some(pair) => pair,
|
Some(pair) => pair,
|
||||||
None => {
|
None => {
|
||||||
|
@ -163,7 +162,7 @@ impl<A: Accumulator> Ingredient for IngredientImpl<A> {
|
||||||
output_key: Option<crate::Id>,
|
output_key: Option<crate::Id>,
|
||||||
) {
|
) {
|
||||||
assert!(output_key.is_none());
|
assert!(output_key.is_none());
|
||||||
let current_revision = db.zalsa().runtime().current_revision();
|
let current_revision = db.zalsa().current_revision();
|
||||||
if let Some(mut v) = self.map.get_mut(&executor) {
|
if let Some(mut v) = self.map.get_mut(&executor) {
|
||||||
// The value is still valid in the new revision.
|
// The value is still valid in the new revision.
|
||||||
v.produced_at = current_revision;
|
v.produced_at = current_revision;
|
||||||
|
|
|
@ -2,8 +2,9 @@ use crate::{
|
||||||
durability::Durability,
|
durability::Durability,
|
||||||
hash::{FxIndexMap, FxIndexSet},
|
hash::{FxIndexMap, FxIndexSet},
|
||||||
key::{DatabaseKeyIndex, DependencyIndex},
|
key::{DatabaseKeyIndex, DependencyIndex},
|
||||||
|
local_state::EMPTY_DEPENDENCIES,
|
||||||
tracked_struct::Disambiguator,
|
tracked_struct::Disambiguator,
|
||||||
Cycle, Revision, Runtime,
|
Cycle, Revision,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::local_state::{EdgeKind, QueryEdges, QueryOrigin, QueryRevisions};
|
use super::local_state::{EdgeKind, QueryEdges, QueryOrigin, QueryRevisions};
|
||||||
|
@ -86,9 +87,9 @@ impl ActiveQuery {
|
||||||
self.input_outputs.contains(&(EdgeKind::Output, key))
|
self.input_outputs.contains(&(EdgeKind::Output, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn revisions(&self, runtime: &Runtime) -> QueryRevisions {
|
pub(crate) fn revisions(&self) -> QueryRevisions {
|
||||||
let input_outputs = if self.input_outputs.is_empty() {
|
let input_outputs = if self.input_outputs.is_empty() {
|
||||||
runtime.empty_dependencies()
|
EMPTY_DEPENDENCIES.clone()
|
||||||
} else {
|
} else {
|
||||||
self.input_outputs.iter().copied().collect()
|
self.input_outputs.iter().copied().collect()
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,9 +21,9 @@ pub trait Database: ZalsaDatabase + AsDynDatabase {
|
||||||
/// will block until that snapshot is dropped -- if that snapshot
|
/// will block until that snapshot is dropped -- if that snapshot
|
||||||
/// is owned by the current thread, this could trigger deadlock.
|
/// is owned by the current thread, this could trigger deadlock.
|
||||||
fn synthetic_write(&mut self, durability: Durability) {
|
fn synthetic_write(&mut self, durability: Durability) {
|
||||||
let runtime = self.zalsa_mut().runtime_mut();
|
let zalsa_mut = self.zalsa_mut();
|
||||||
runtime.new_revision();
|
zalsa_mut.new_revision();
|
||||||
runtime.report_tracked_write(durability);
|
zalsa_mut.report_tracked_write(durability);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reports that the query depends on some state unknown to salsa.
|
/// Reports that the query depends on some state unknown to salsa.
|
||||||
|
@ -33,7 +33,7 @@ pub trait Database: ZalsaDatabase + AsDynDatabase {
|
||||||
fn report_untracked_read(&self) {
|
fn report_untracked_read(&self) {
|
||||||
let db = self.as_dyn_database();
|
let db = self.as_dyn_database();
|
||||||
local_state::attach(db, |state| {
|
local_state::attach(db, |state| {
|
||||||
state.report_untracked_read(db.zalsa().runtime().current_revision())
|
state.report_untracked_read(db.zalsa().current_revision())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ impl<T: Database> AsDynDatabase for T {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_revision<Db: ?Sized + Database>(db: &Db) -> Revision {
|
pub fn current_revision<Db: ?Sized + Database>(db: &Db) -> Revision {
|
||||||
db.zalsa().runtime().current_revision()
|
db.zalsa().current_revision()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Database {
|
impl dyn Database {
|
||||||
|
|
|
@ -16,7 +16,7 @@ where
|
||||||
{
|
{
|
||||||
local_state::attach(db, |local_state| {
|
local_state::attach(db, |local_state| {
|
||||||
let zalsa = db.zalsa();
|
let zalsa = db.zalsa();
|
||||||
let current_revision = zalsa.runtime().current_revision();
|
let current_revision = zalsa.current_revision();
|
||||||
|
|
||||||
let Some(accumulator) = <accumulator::IngredientImpl<A>>::from_db(db) else {
|
let Some(accumulator) = <accumulator::IngredientImpl<A>>::from_db(db) else {
|
||||||
return vec![];
|
return vec![];
|
||||||
|
|
|
@ -27,8 +27,7 @@ where
|
||||||
opt_old_memo: Option<Arc<Memo<C::Output<'_>>>>,
|
opt_old_memo: Option<Arc<Memo<C::Output<'_>>>>,
|
||||||
) -> StampedValue<&C::Output<'db>> {
|
) -> StampedValue<&C::Output<'db>> {
|
||||||
let zalsa = db.zalsa();
|
let zalsa = db.zalsa();
|
||||||
let runtime = zalsa.runtime();
|
let revision_now = zalsa.current_revision();
|
||||||
let revision_now = runtime.current_revision();
|
|
||||||
let database_key_index = active_query.database_key_index;
|
let database_key_index = active_query.database_key_index;
|
||||||
|
|
||||||
tracing::info!("{:?}: executing query", database_key_index);
|
tracing::info!("{:?}: executing query", database_key_index);
|
||||||
|
@ -68,16 +67,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut revisions = active_query.pop(runtime);
|
let mut revisions = active_query.pop();
|
||||||
|
|
||||||
// We assume that query is side-effect free -- that is, does
|
|
||||||
// not mutate the "inputs" to the query system. Sanity check
|
|
||||||
// that assumption here, at least to the best of our ability.
|
|
||||||
assert_eq!(
|
|
||||||
runtime.current_revision(),
|
|
||||||
revision_now,
|
|
||||||
"revision altered during query execution",
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the new value is equal to the old one, then it didn't
|
// If the new value is equal to the old one, then it didn't
|
||||||
// really change, even if some of its inputs have. So we can
|
// really change, even if some of its inputs have. So we can
|
||||||
|
|
|
@ -63,8 +63,8 @@ where
|
||||||
let memo_guard = self.memo_map.get(key);
|
let memo_guard = self.memo_map.get(key);
|
||||||
if let Some(memo) = &memo_guard {
|
if let Some(memo) = &memo_guard {
|
||||||
if memo.value.is_some() {
|
if memo.value.is_some() {
|
||||||
let runtime = db.zalsa().runtime();
|
let zalsa = db.zalsa();
|
||||||
if self.shallow_verify_memo(db, runtime, self.database_key_index(key), memo) {
|
if self.shallow_verify_memo(db, zalsa, self.database_key_index(key), memo) {
|
||||||
let value = unsafe {
|
let value = unsafe {
|
||||||
// Unsafety invariant: memo is present in memo_map
|
// Unsafety invariant: memo is present in memo_map
|
||||||
self.extend_memo_lifetime(memo).unwrap()
|
self.extend_memo_lifetime(memo).unwrap()
|
||||||
|
|
|
@ -4,8 +4,8 @@ use crate::{
|
||||||
key::DatabaseKeyIndex,
|
key::DatabaseKeyIndex,
|
||||||
local_state::{self, ActiveQueryGuard, EdgeKind, LocalState, QueryOrigin},
|
local_state::{self, ActiveQueryGuard, EdgeKind, LocalState, QueryOrigin},
|
||||||
runtime::StampedValue,
|
runtime::StampedValue,
|
||||||
storage::ZalsaDatabase as _,
|
storage::{Zalsa, ZalsaDatabase as _},
|
||||||
AsDynDatabase as _, Id, Revision, Runtime,
|
AsDynDatabase as _, Id, Revision,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{memo::Memo, Configuration, IngredientImpl};
|
use super::{memo::Memo, Configuration, IngredientImpl};
|
||||||
|
@ -21,7 +21,7 @@ where
|
||||||
revision: Revision,
|
revision: Revision,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
local_state::attach(db.as_dyn_database(), |local_state| {
|
local_state::attach(db.as_dyn_database(), |local_state| {
|
||||||
let runtime = db.zalsa().runtime();
|
let zalsa = db.zalsa();
|
||||||
local_state.unwind_if_revision_cancelled(db.as_dyn_database());
|
local_state.unwind_if_revision_cancelled(db.as_dyn_database());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -34,7 +34,7 @@ where
|
||||||
// Check if we have a verified version: this is the hot path.
|
// Check if we have a verified version: this is the hot path.
|
||||||
let memo_guard = self.memo_map.get(key);
|
let memo_guard = self.memo_map.get(key);
|
||||||
if let Some(memo) = &memo_guard {
|
if let Some(memo) = &memo_guard {
|
||||||
if self.shallow_verify_memo(db, runtime, database_key_index, memo) {
|
if self.shallow_verify_memo(db, zalsa, database_key_index, memo) {
|
||||||
return memo.revisions.changed_at > revision;
|
return memo.revisions.changed_at > revision;
|
||||||
}
|
}
|
||||||
drop(memo_guard); // release the arc-swap guard before cold path
|
drop(memo_guard); // release the arc-swap guard before cold path
|
||||||
|
@ -102,12 +102,12 @@ where
|
||||||
pub(super) fn shallow_verify_memo(
|
pub(super) fn shallow_verify_memo(
|
||||||
&self,
|
&self,
|
||||||
db: &C::DbView,
|
db: &C::DbView,
|
||||||
runtime: &Runtime,
|
zalsa: &dyn Zalsa,
|
||||||
database_key_index: DatabaseKeyIndex,
|
database_key_index: DatabaseKeyIndex,
|
||||||
memo: &Memo<C::Output<'_>>,
|
memo: &Memo<C::Output<'_>>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let verified_at = memo.verified_at.load();
|
let verified_at = memo.verified_at.load();
|
||||||
let revision_now = runtime.current_revision();
|
let revision_now = zalsa.current_revision();
|
||||||
|
|
||||||
tracing::debug!("{database_key_index:?}: shallow_verify_memo(memo = {memo:#?})",);
|
tracing::debug!("{database_key_index:?}: shallow_verify_memo(memo = {memo:#?})",);
|
||||||
|
|
||||||
|
@ -116,10 +116,10 @@ where
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if memo.check_durability(runtime) {
|
if memo.check_durability(zalsa) {
|
||||||
// No input of the suitable durability has changed since last verified.
|
// No input of the suitable durability has changed since last verified.
|
||||||
let db = db.as_dyn_database();
|
let db = db.as_dyn_database();
|
||||||
memo.mark_as_verified(db, runtime, database_key_index);
|
memo.mark_as_verified(db, revision_now, database_key_index);
|
||||||
memo.mark_outputs_as_verified(db, database_key_index);
|
memo.mark_outputs_as_verified(db, database_key_index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -141,12 +141,12 @@ where
|
||||||
old_memo: &Memo<C::Output<'_>>,
|
old_memo: &Memo<C::Output<'_>>,
|
||||||
active_query: &ActiveQueryGuard<'_>,
|
active_query: &ActiveQueryGuard<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let runtime = db.zalsa().runtime();
|
let zalsa = db.zalsa();
|
||||||
let database_key_index = active_query.database_key_index;
|
let database_key_index = active_query.database_key_index;
|
||||||
|
|
||||||
tracing::debug!("{database_key_index:?}: deep_verify_memo(old_memo = {old_memo:#?})",);
|
tracing::debug!("{database_key_index:?}: deep_verify_memo(old_memo = {old_memo:#?})",);
|
||||||
|
|
||||||
if self.shallow_verify_memo(db, runtime, database_key_index, old_memo) {
|
if self.shallow_verify_memo(db, zalsa, database_key_index, old_memo) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +215,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
old_memo.mark_as_verified(db.as_dyn_database(), runtime, database_key_index);
|
old_memo.mark_as_verified(
|
||||||
|
db.as_dyn_database(),
|
||||||
|
zalsa.current_revision(),
|
||||||
|
database_key_index,
|
||||||
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ use arc_swap::{ArcSwap, Guard};
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
hash::FxDashMap, key::DatabaseKeyIndex, local_state::QueryRevisions, Event, EventKind, Id,
|
hash::FxDashMap, key::DatabaseKeyIndex, local_state::QueryRevisions, storage::Zalsa, Event,
|
||||||
Revision, Runtime,
|
EventKind, Id, Revision,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Configuration;
|
use super::Configuration;
|
||||||
|
@ -129,8 +129,8 @@ impl<V> Memo<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// True if this memo is known not to have changed based on its durability.
|
/// True if this memo is known not to have changed based on its durability.
|
||||||
pub(super) fn check_durability(&self, runtime: &Runtime) -> bool {
|
pub(super) fn check_durability(&self, zalsa: &dyn Zalsa) -> bool {
|
||||||
let last_changed = runtime.last_changed_revision(self.revisions.durability);
|
let last_changed = zalsa.last_changed_revision(self.revisions.durability);
|
||||||
let verified_at = self.verified_at.load();
|
let verified_at = self.verified_at.load();
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"check_durability(last_changed={:?} <= verified_at={:?}) = {:?}",
|
"check_durability(last_changed={:?} <= verified_at={:?}) = {:?}",
|
||||||
|
@ -146,7 +146,7 @@ impl<V> Memo<V> {
|
||||||
pub(super) fn mark_as_verified(
|
pub(super) fn mark_as_verified(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn crate::Database,
|
db: &dyn crate::Database,
|
||||||
runtime: &crate::Runtime,
|
revision_now: Revision,
|
||||||
database_key_index: DatabaseKeyIndex,
|
database_key_index: DatabaseKeyIndex,
|
||||||
) {
|
) {
|
||||||
db.salsa_event(Event {
|
db.salsa_event(Event {
|
||||||
|
@ -156,7 +156,7 @@ impl<V> Memo<V> {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
self.verified_at.store(runtime.current_revision());
|
self.verified_at.store(revision_now);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn mark_outputs_as_verified(
|
pub(super) fn mark_outputs_as_verified(
|
||||||
|
|
|
@ -64,7 +64,7 @@ where
|
||||||
// - a result that is verified in the current revision, because it was set, which will use the set value
|
// - a result that is verified in the current revision, because it was set, which will use the set value
|
||||||
// - a result that is NOT verified and has untracked inputs, which will re-execute (and likely panic)
|
// - a result that is NOT verified and has untracked inputs, which will re-execute (and likely panic)
|
||||||
|
|
||||||
let revision = db.zalsa().runtime().current_revision();
|
let revision = db.zalsa().current_revision();
|
||||||
let mut revisions = QueryRevisions {
|
let mut revisions = QueryRevisions {
|
||||||
changed_at: current_deps.changed_at,
|
changed_at: current_deps.changed_at,
|
||||||
durability: current_deps.durability,
|
durability: current_deps.durability,
|
||||||
|
@ -101,7 +101,7 @@ where
|
||||||
executor: DatabaseKeyIndex,
|
executor: DatabaseKeyIndex,
|
||||||
key: Id,
|
key: Id,
|
||||||
) {
|
) {
|
||||||
let runtime = db.zalsa().runtime();
|
let zalsa = db.zalsa();
|
||||||
|
|
||||||
let memo = match self.memo_map.get(key) {
|
let memo = match self.memo_map.get(key) {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
|
@ -119,6 +119,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let database_key_index = self.database_key_index(key);
|
let database_key_index = self.database_key_index(key);
|
||||||
memo.mark_as_verified(db.as_dyn_database(), runtime, database_key_index);
|
memo.mark_as_verified(
|
||||||
|
db.as_dyn_database(),
|
||||||
|
zalsa.current_revision(),
|
||||||
|
database_key_index,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ impl SyncMap {
|
||||||
local_state: &LocalState,
|
local_state: &LocalState,
|
||||||
database_key_index: DatabaseKeyIndex,
|
database_key_index: DatabaseKeyIndex,
|
||||||
) -> Option<ClaimGuard<'me>> {
|
) -> Option<ClaimGuard<'me>> {
|
||||||
let runtime = db.zalsa().runtime();
|
let runtime = db.zalsa().runtimex();
|
||||||
let thread_id = std::thread::current().id();
|
let thread_id = std::thread::current().id();
|
||||||
match self.sync_map.entry(database_key_index.key_index) {
|
match self.sync_map.entry(database_key_index.key_index) {
|
||||||
dashmap::mapref::entry::Entry::Vacant(entry) => {
|
dashmap::mapref::entry::Entry::Vacant(entry) => {
|
||||||
|
|
|
@ -78,7 +78,7 @@ impl<Db: Database> Handle<Db> {
|
||||||
/// same database!
|
/// same database!
|
||||||
fn cancel_others(&mut self) {
|
fn cancel_others(&mut self) {
|
||||||
let zalsa = self.db().zalsa();
|
let zalsa = self.db().zalsa();
|
||||||
zalsa.runtime().set_cancellation_flag();
|
zalsa.set_cancellation_flag();
|
||||||
|
|
||||||
self.db().salsa_event(Event {
|
self.db().salsa_event(Event {
|
||||||
thread_id: std::thread::current().id(),
|
thread_id: std::thread::current().id(),
|
||||||
|
|
|
@ -19,7 +19,6 @@ use crate::{
|
||||||
key::{DatabaseKeyIndex, DependencyIndex},
|
key::{DatabaseKeyIndex, DependencyIndex},
|
||||||
local_state::{self, QueryOrigin},
|
local_state::{self, QueryOrigin},
|
||||||
plumbing::{Jar, Stamp},
|
plumbing::{Jar, Stamp},
|
||||||
runtime::Runtime,
|
|
||||||
storage::IngredientIndex,
|
storage::IngredientIndex,
|
||||||
Database, Durability, Id, Revision,
|
Database, Durability, Id, Revision,
|
||||||
};
|
};
|
||||||
|
@ -121,18 +120,17 @@ impl<C: Configuration> IngredientImpl<C> {
|
||||||
/// * `setter`, function that modifies the fields tuple; should only modify the element for `field_index`
|
/// * `setter`, function that modifies the fields tuple; should only modify the element for `field_index`
|
||||||
pub fn set_field<R>(
|
pub fn set_field<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
runtime: &mut Runtime,
|
current_revision: Revision,
|
||||||
id: C::Struct,
|
id: C::Struct,
|
||||||
field_index: usize,
|
field_index: usize,
|
||||||
durability: Durability,
|
durability: Durability,
|
||||||
setter: impl FnOnce(&mut C::Fields) -> R,
|
setter: impl FnOnce(&mut C::Fields) -> R,
|
||||||
) -> R {
|
) -> R {
|
||||||
let revision = runtime.current_revision();
|
|
||||||
let id: Id = id.as_id();
|
let id: Id = id.as_id();
|
||||||
let mut r = self.struct_map.update(id);
|
let mut r = self.struct_map.update(id);
|
||||||
let stamp = &mut r.stamps[field_index];
|
let stamp = &mut r.stamps[field_index];
|
||||||
stamp.durability = durability;
|
stamp.durability = durability;
|
||||||
stamp.changed_at = revision;
|
stamp.changed_at = current_revision;
|
||||||
setter(&mut r.fields)
|
setter(&mut r.fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::input::{Configuration, IngredientImpl};
|
use crate::input::{Configuration, IngredientImpl};
|
||||||
use crate::{Durability, Runtime};
|
use crate::{Durability, Revision};
|
||||||
|
|
||||||
/// Setter for a field of an input.
|
/// Setter for a field of an input.
|
||||||
pub trait Setter: Sized {
|
pub trait Setter: Sized {
|
||||||
|
@ -12,7 +12,7 @@ pub trait Setter: Sized {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct SetterImpl<'setter, C: Configuration, S, F> {
|
pub struct SetterImpl<'setter, C: Configuration, S, F> {
|
||||||
runtime: &'setter mut Runtime,
|
current_revision: Revision,
|
||||||
id: C::Struct,
|
id: C::Struct,
|
||||||
ingredient: &'setter mut IngredientImpl<C>,
|
ingredient: &'setter mut IngredientImpl<C>,
|
||||||
durability: Durability,
|
durability: Durability,
|
||||||
|
@ -27,14 +27,14 @@ where
|
||||||
S: FnOnce(&mut C::Fields, F) -> F,
|
S: FnOnce(&mut C::Fields, F) -> F,
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
runtime: &'setter mut Runtime,
|
current_revision: Revision,
|
||||||
id: C::Struct,
|
id: C::Struct,
|
||||||
field_index: usize,
|
field_index: usize,
|
||||||
ingredient: &'setter mut IngredientImpl<C>,
|
ingredient: &'setter mut IngredientImpl<C>,
|
||||||
setter: S,
|
setter: S,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
SetterImpl {
|
SetterImpl {
|
||||||
runtime,
|
current_revision,
|
||||||
id,
|
id,
|
||||||
field_index,
|
field_index,
|
||||||
ingredient,
|
ingredient,
|
||||||
|
@ -59,7 +59,7 @@ where
|
||||||
|
|
||||||
fn to(self, value: F) -> F {
|
fn to(self, value: F) -> F {
|
||||||
let Self {
|
let Self {
|
||||||
runtime,
|
current_revision,
|
||||||
id,
|
id,
|
||||||
ingredient,
|
ingredient,
|
||||||
durability,
|
durability,
|
||||||
|
@ -68,7 +68,7 @@ where
|
||||||
phantom: _,
|
phantom: _,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ingredient.set_field(runtime, id, field_index, durability, |tuple| {
|
ingredient.set_field(current_revision, id, field_index, durability, |tuple| {
|
||||||
setter(tuple, value)
|
setter(tuple, value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ use crate::Database;
|
||||||
use crate::Event;
|
use crate::Event;
|
||||||
use crate::EventKind;
|
use crate::EventKind;
|
||||||
use crate::Revision;
|
use crate::Revision;
|
||||||
use crate::Runtime;
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
@ -321,21 +320,20 @@ impl LocalState {
|
||||||
/// `salsa_event` is emitted when this method is called, so that should be
|
/// `salsa_event` is emitted when this method is called, so that should be
|
||||||
/// used instead.
|
/// used instead.
|
||||||
pub(crate) fn unwind_if_revision_cancelled(&self, db: &dyn Database) {
|
pub(crate) fn unwind_if_revision_cancelled(&self, db: &dyn Database) {
|
||||||
let runtime = db.zalsa().runtime();
|
|
||||||
let thread_id = std::thread::current().id();
|
let thread_id = std::thread::current().id();
|
||||||
db.salsa_event(Event {
|
db.salsa_event(Event {
|
||||||
thread_id,
|
thread_id,
|
||||||
|
|
||||||
kind: EventKind::WillCheckCancellation,
|
kind: EventKind::WillCheckCancellation,
|
||||||
});
|
});
|
||||||
if runtime.load_cancellation_flag() {
|
let zalsa = db.zalsa();
|
||||||
self.unwind_cancelled(runtime);
|
if zalsa.load_cancellation_flag() {
|
||||||
|
self.unwind_cancelled(zalsa.current_revision());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
pub(crate) fn unwind_cancelled(&self, runtime: &Runtime) {
|
pub(crate) fn unwind_cancelled(&self, current_revision: Revision) {
|
||||||
let current_revision = runtime.current_revision();
|
|
||||||
self.report_untracked_read(current_revision);
|
self.report_untracked_read(current_revision);
|
||||||
Cancelled::PendingWrite.throw();
|
Cancelled::PendingWrite.throw();
|
||||||
}
|
}
|
||||||
|
@ -414,6 +412,10 @@ pub enum EdgeKind {
|
||||||
Output,
|
Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub(crate) static ref EMPTY_DEPENDENCIES: Arc<[(EdgeKind, DependencyIndex)]> = Arc::new([]);
|
||||||
|
}
|
||||||
|
|
||||||
/// The edges between a memoized value and other queries in the dependency graph.
|
/// The edges between a memoized value and other queries in the dependency graph.
|
||||||
/// These edges include both dependency edges
|
/// These edges include both dependency edges
|
||||||
/// e.g., when creating the memoized value for Q0 executed another function Q1)
|
/// e.g., when creating the memoized value for Q0 executed another function Q1)
|
||||||
|
@ -497,14 +499,14 @@ impl ActiveQueryGuard<'_> {
|
||||||
/// which summarizes the other queries that were accessed during this
|
/// which summarizes the other queries that were accessed during this
|
||||||
/// query's execution.
|
/// query's execution.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn pop(self, runtime: &Runtime) -> QueryRevisions {
|
pub(crate) fn pop(self) -> QueryRevisions {
|
||||||
// Extract accumulated inputs.
|
// Extract accumulated inputs.
|
||||||
let popped_query = self.complete();
|
let popped_query = self.complete();
|
||||||
|
|
||||||
// If this frame were a cycle participant, it would have unwound.
|
// If this frame were a cycle participant, it would have unwound.
|
||||||
assert!(popped_query.cycle.is_none());
|
assert!(popped_query.cycle.is_none());
|
||||||
|
|
||||||
popped_query.revisions(runtime)
|
popped_query.revisions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the active query is registered as a cycle participant, remove and
|
/// If the active query is registered as a cycle participant, remove and
|
||||||
|
|
|
@ -8,13 +8,9 @@ use crossbeam::atomic::AtomicCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
active_query::ActiveQuery,
|
active_query::ActiveQuery, cycle::CycleRecoveryStrategy, durability::Durability,
|
||||||
cycle::CycleRecoveryStrategy,
|
key::DatabaseKeyIndex, local_state::LocalState, revision::AtomicRevision, Cancelled, Cycle,
|
||||||
durability::Durability,
|
Database, Event, EventKind, Revision,
|
||||||
key::{DatabaseKeyIndex, DependencyIndex},
|
|
||||||
local_state::{EdgeKind, LocalState},
|
|
||||||
revision::AtomicRevision,
|
|
||||||
Cancelled, Cycle, Database, Event, EventKind, Revision,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::dependency_graph::DependencyGraph;
|
use self::dependency_graph::DependencyGraph;
|
||||||
|
@ -25,9 +21,6 @@ pub struct Runtime {
|
||||||
/// Stores the next id to use for a snapshotted runtime (starts at 1).
|
/// Stores the next id to use for a snapshotted runtime (starts at 1).
|
||||||
next_id: AtomicUsize,
|
next_id: AtomicUsize,
|
||||||
|
|
||||||
/// Vector we can clone
|
|
||||||
empty_dependencies: Arc<[(EdgeKind, DependencyIndex)]>,
|
|
||||||
|
|
||||||
/// Set to true when the current revision has been canceled.
|
/// Set to true when the current revision has been canceled.
|
||||||
/// This is done when we an input is being changed. The flag
|
/// This is done when we an input is being changed. The flag
|
||||||
/// is set back to false once the input has been changed.
|
/// is set back to false once the input has been changed.
|
||||||
|
@ -89,7 +82,6 @@ impl Default for Runtime {
|
||||||
.map(|_| AtomicRevision::start())
|
.map(|_| AtomicRevision::start())
|
||||||
.collect(),
|
.collect(),
|
||||||
next_id: AtomicUsize::new(1),
|
next_id: AtomicUsize::new(1),
|
||||||
empty_dependencies: None.into_iter().collect(),
|
|
||||||
revision_canceled: Default::default(),
|
revision_canceled: Default::default(),
|
||||||
dependency_graph: Default::default(),
|
dependency_graph: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -112,10 +104,6 @@ impl Runtime {
|
||||||
self.revisions[0].load()
|
self.revisions[0].load()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn empty_dependencies(&self) -> Arc<[(EdgeKind, DependencyIndex)]> {
|
|
||||||
self.empty_dependencies.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reports that an input with durability `durability` changed.
|
/// Reports that an input with durability `durability` changed.
|
||||||
/// This will update the 'last changed at' values for every durability
|
/// This will update the 'last changed at' values for every durability
|
||||||
/// less than or equal to `durability` to the current revision.
|
/// less than or equal to `durability` to the current revision.
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::ingredient::{Ingredient, Jar};
|
||||||
use crate::nonce::{Nonce, NonceGenerator};
|
use crate::nonce::{Nonce, NonceGenerator};
|
||||||
use crate::runtime::Runtime;
|
use crate::runtime::Runtime;
|
||||||
use crate::views::{Views, ViewsOf};
|
use crate::views::{Views, ViewsOf};
|
||||||
use crate::Database;
|
use crate::{Database, Durability, Revision};
|
||||||
|
|
||||||
pub fn views<Db: ?Sized + Database>(db: &Db) -> &Views {
|
pub fn views<Db: ?Sized + Database>(db: &Db) -> &Views {
|
||||||
db.zalsa().views()
|
db.zalsa().views()
|
||||||
|
@ -55,17 +55,36 @@ pub trait Zalsa {
|
||||||
/// Gets an `&`-ref to an ingredient by index
|
/// Gets an `&`-ref to an ingredient by index
|
||||||
fn lookup_ingredient(&self, index: IngredientIndex) -> &dyn Ingredient;
|
fn lookup_ingredient(&self, index: IngredientIndex) -> &dyn Ingredient;
|
||||||
|
|
||||||
/// Gets an `&mut`-ref to an ingredient by index; also returns the runtime for further use
|
/// Gets an `&mut`-ref to an ingredient by index.
|
||||||
|
///
|
||||||
|
/// **Triggers a new revision.** Returns the `&mut` reference
|
||||||
|
/// along with the new revision index.
|
||||||
fn lookup_ingredient_mut(
|
fn lookup_ingredient_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: IngredientIndex,
|
index: IngredientIndex,
|
||||||
) -> (&mut dyn Ingredient, &mut Runtime);
|
) -> (&mut dyn Ingredient, Revision);
|
||||||
|
|
||||||
/// Gets the salsa runtime
|
fn runtimex(&self) -> &Runtime;
|
||||||
fn runtime(&self) -> &Runtime;
|
|
||||||
|
|
||||||
/// Gets the salsa runtime
|
/// Return the current revision
|
||||||
fn runtime_mut(&mut self) -> &mut Runtime;
|
fn current_revision(&self) -> Revision;
|
||||||
|
|
||||||
|
/// Increment revision counter.
|
||||||
|
///
|
||||||
|
/// **Triggers a new revision.**
|
||||||
|
fn new_revision(&mut self) -> Revision;
|
||||||
|
|
||||||
|
/// Return the time when an input of durability `durability` last changed
|
||||||
|
fn last_changed_revision(&self, durability: Durability) -> Revision;
|
||||||
|
|
||||||
|
/// True if any threads have signalled for cancellation
|
||||||
|
fn load_cancellation_flag(&self) -> bool;
|
||||||
|
|
||||||
|
/// Signal for cancellation, indicating current thread is trying to get unique access.
|
||||||
|
fn set_cancellation_flag(&self);
|
||||||
|
|
||||||
|
/// Reports a (synthetic) tracked write to "some input of the given durability".
|
||||||
|
fn report_tracked_write(&mut self, durability: Durability);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Db: Database> Zalsa for Storage<Db> {
|
impl<Db: Database> Zalsa for Storage<Db> {
|
||||||
|
@ -119,19 +138,11 @@ impl<Db: Database> Zalsa for Storage<Db> {
|
||||||
&**self.ingredients_vec.get(index.as_usize()).unwrap()
|
&**self.ingredients_vec.get(index.as_usize()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime(&self) -> &Runtime {
|
|
||||||
&self.runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
fn runtime_mut(&mut self) -> &mut Runtime {
|
|
||||||
&mut self.runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_ingredient_mut(
|
fn lookup_ingredient_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: IngredientIndex,
|
index: IngredientIndex,
|
||||||
) -> (&mut dyn Ingredient, &mut Runtime) {
|
) -> (&mut dyn Ingredient, Revision) {
|
||||||
self.runtime.new_revision();
|
let new_revision = self.runtime.new_revision();
|
||||||
|
|
||||||
for index in self.ingredients_requiring_reset.iter() {
|
for index in self.ingredients_requiring_reset.iter() {
|
||||||
self.ingredients_vec
|
self.ingredients_vec
|
||||||
|
@ -142,9 +153,37 @@ impl<Db: Database> Zalsa for Storage<Db> {
|
||||||
|
|
||||||
(
|
(
|
||||||
&mut **self.ingredients_vec.get_mut(index.as_usize()).unwrap(),
|
&mut **self.ingredients_vec.get_mut(index.as_usize()).unwrap(),
|
||||||
&mut self.runtime,
|
new_revision,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_revision(&self) -> Revision {
|
||||||
|
self.runtime.current_revision()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_cancellation_flag(&self) -> bool {
|
||||||
|
self.runtime.load_cancellation_flag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn report_tracked_write(&mut self, durability: Durability) {
|
||||||
|
self.runtime.report_tracked_write(durability)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtimex(&self) -> &Runtime {
|
||||||
|
&self.runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_changed_revision(&self, durability: Durability) -> Revision {
|
||||||
|
self.runtime.last_changed_revision(durability)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cancellation_flag(&self) {
|
||||||
|
self.runtime.set_cancellation_flag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_revision(&mut self) -> Revision {
|
||||||
|
self.runtime.new_revision()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Nonce type representing the underlying database storage.
|
/// Nonce type representing the underlying database storage.
|
||||||
|
|
|
@ -307,7 +307,7 @@ where
|
||||||
let (id, new_id) = self.intern(entity_key);
|
let (id, new_id) = self.intern(entity_key);
|
||||||
local_state.add_output(self.database_key_index(id).into());
|
local_state.add_output(self.database_key_index(id).into());
|
||||||
|
|
||||||
let current_revision = zalsa.runtime().current_revision();
|
let current_revision = zalsa.current_revision();
|
||||||
if new_id {
|
if new_id {
|
||||||
// This is a new tracked struct, so create an entry in the struct map.
|
// This is a new tracked struct, so create an entry in the struct map.
|
||||||
|
|
||||||
|
@ -379,7 +379,7 @@ where
|
||||||
///
|
///
|
||||||
/// If the struct has not been created in this revision.
|
/// If the struct has not been created in this revision.
|
||||||
pub fn lookup_struct<'db>(&'db self, db: &'db dyn Database, id: Id) -> C::Struct<'db> {
|
pub fn lookup_struct<'db>(&'db self, db: &'db dyn Database, id: Id) -> C::Struct<'db> {
|
||||||
let current_revision = db.zalsa().runtime().current_revision();
|
let current_revision = db.zalsa().current_revision();
|
||||||
self.struct_map.get(current_revision, id)
|
self.struct_map.get(current_revision, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,9 +458,9 @@ where
|
||||||
_executor: DatabaseKeyIndex,
|
_executor: DatabaseKeyIndex,
|
||||||
output_key: Option<crate::Id>,
|
output_key: Option<crate::Id>,
|
||||||
) {
|
) {
|
||||||
let runtime = db.zalsa().runtime();
|
let current_revision = db.zalsa().current_revision();
|
||||||
let output_key = output_key.unwrap();
|
let output_key = output_key.unwrap();
|
||||||
self.struct_map.validate(runtime, output_key);
|
self.struct_map.validate(current_revision, output_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_stale_output(
|
fn remove_stale_output(
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
use crossbeam::queue::SegQueue;
|
use crossbeam::queue::SegQueue;
|
||||||
use dashmap::mapref::one::RefMut;
|
use dashmap::mapref::one::RefMut;
|
||||||
|
|
||||||
use crate::{alloc::Alloc, hash::FxDashMap, Id, Revision, Runtime};
|
use crate::{alloc::Alloc, hash::FxDashMap, Id, Revision};
|
||||||
|
|
||||||
use super::{Configuration, KeyStruct, Value};
|
use super::{Configuration, KeyStruct, Value};
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ where
|
||||||
unsafe { C::struct_from_raw(pointer) }
|
unsafe { C::struct_from_raw(pointer) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate<'db>(&'db self, runtime: &'db Runtime, id: Id) {
|
pub fn validate<'db>(&'db self, current_revision: Revision, id: Id) {
|
||||||
let mut data = self.map.get_mut(&id).unwrap();
|
let mut data = self.map.get_mut(&id).unwrap();
|
||||||
|
|
||||||
// UNSAFE: We never permit `&`-access in the current revision until data.created_at
|
// UNSAFE: We never permit `&`-access in the current revision until data.created_at
|
||||||
|
@ -107,7 +107,6 @@ where
|
||||||
let data = unsafe { data.as_mut() };
|
let data = unsafe { data.as_mut() };
|
||||||
|
|
||||||
// Never update a struct twice in the same revision.
|
// Never update a struct twice in the same revision.
|
||||||
let current_revision = runtime.current_revision();
|
|
||||||
assert!(data.created_at < current_revision);
|
assert!(data.created_at < current_revision);
|
||||||
data.created_at = current_revision;
|
data.created_at = current_revision;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ where
|
||||||
/// The caller is responible for selecting the appropriate element.
|
/// The caller is responible for selecting the appropriate element.
|
||||||
pub fn field<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Fields<'db> {
|
pub fn field<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Fields<'db> {
|
||||||
local_state::attach(db, |local_state| {
|
local_state::attach(db, |local_state| {
|
||||||
let current_revision = db.zalsa().runtime().current_revision();
|
let current_revision = db.zalsa().current_revision();
|
||||||
let data = self.struct_map.get(current_revision, id);
|
let data = self.struct_map.get(current_revision, id);
|
||||||
let data = C::deref_struct(data);
|
let data = C::deref_struct(data);
|
||||||
let changed_at = data.revisions[self.field_index];
|
let changed_at = data.revisions[self.field_index];
|
||||||
|
@ -85,7 +85,7 @@ where
|
||||||
input: Option<Id>,
|
input: Option<Id>,
|
||||||
revision: crate::Revision,
|
revision: crate::Revision,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let current_revision = db.zalsa().runtime().current_revision();
|
let current_revision = db.zalsa().current_revision();
|
||||||
let id = input.unwrap();
|
let id = input.unwrap();
|
||||||
let data = self.struct_map.get(current_revision, id);
|
let data = self.struct_map.get(current_revision, id);
|
||||||
let data = C::deref_struct(data);
|
let data = C::deref_struct(data);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue