(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:
Niko Matsakis 2024-07-27 10:53:15 +00:00
parent 596461c213
commit 1842b1dfbb
21 changed files with 136 additions and 111 deletions

View file

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

View file

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

View file

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

View file

@ -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()
}; };

View file

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

View file

@ -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![];

View file

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

View file

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

View file

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

View file

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

View file

@ -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,
);
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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