mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-08-04 19:08:32 +00:00
remove RuntimeId and use ThreadId
This commit is contained in:
parent
7c2bbe811e
commit
3d2b2d3a65
13 changed files with 59 additions and 445 deletions
|
@ -268,8 +268,8 @@ fn fix_bad_variable_in_function() {
|
|||
"#]],
|
||||
expect![[r#"
|
||||
[
|
||||
"Event: Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: parse_statements(0) } }",
|
||||
"Event: Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: type_check_function(0) } }",
|
||||
"Event: Event { thread_id: ThreadId(11), kind: WillExecute { database_key: parse_statements(0) } }",
|
||||
"Event: Event { thread_id: ThreadId(11), kind: WillExecute { database_key: type_check_function(0) } }",
|
||||
]
|
||||
"#]],
|
||||
)],
|
||||
|
|
|
@ -174,7 +174,7 @@ impl<A: Accumulator> Ingredient for IngredientImpl<A> {
|
|||
assert!(stale_output_key.is_none());
|
||||
if self.map.remove(&executor).is_some() {
|
||||
db.salsa_event(Event {
|
||||
runtime_id: db.runtime().id(),
|
||||
thread_id: std::thread::current().id(),
|
||||
kind: EventKind::DidDiscardAccumulated {
|
||||
executor_key: executor,
|
||||
accumulator: self.dependency_index(),
|
||||
|
|
18
src/event.rs
18
src/event.rs
|
@ -1,13 +1,14 @@
|
|||
use crate::{key::DatabaseKeyIndex, key::DependencyIndex, runtime::RuntimeId};
|
||||
use std::thread::ThreadId;
|
||||
|
||||
use crate::{key::DatabaseKeyIndex, key::DependencyIndex};
|
||||
|
||||
/// The `Event` struct identifies various notable things that can
|
||||
/// occur during salsa execution. Instances of this struct are given
|
||||
/// to `salsa_event`.
|
||||
#[derive(Debug)]
|
||||
pub struct Event {
|
||||
/// The id of the snapshot that triggered the event. Usually
|
||||
/// 1-to-1 with a thread, as well.
|
||||
pub runtime_id: RuntimeId,
|
||||
/// The id of the thread that triggered the event.
|
||||
pub thread_id: ThreadId,
|
||||
|
||||
/// What sort of event was it.
|
||||
pub kind: EventKind,
|
||||
|
@ -26,18 +27,15 @@ pub enum EventKind {
|
|||
database_key: DatabaseKeyIndex,
|
||||
},
|
||||
|
||||
/// Indicates that another thread (with id `other_runtime_id`) is processing the
|
||||
/// Indicates that another thread (with id `other_thread_id`) is processing the
|
||||
/// given query (`database_key`), so we will block until they
|
||||
/// finish.
|
||||
///
|
||||
/// Executes after we have registered with the other thread but
|
||||
/// before they have answered us.
|
||||
///
|
||||
/// (NB: you can find the `id` of the current thread via the
|
||||
/// `runtime`)
|
||||
WillBlockOn {
|
||||
/// The id of the runtime we will block on.
|
||||
other_runtime_id: RuntimeId,
|
||||
/// The id of the thread we will block on.
|
||||
other_thread_id: ThreadId,
|
||||
|
||||
/// The database-key for the affected value. Implements `Debug`.
|
||||
database_key: DatabaseKeyIndex,
|
||||
|
|
|
@ -269,7 +269,7 @@ where
|
|||
if let Some(origin) = self.delete_memo(id) {
|
||||
let key = self.database_key_index(id);
|
||||
db.salsa_event(Event {
|
||||
runtime_id: db.runtime().id(),
|
||||
thread_id: std::thread::current().id(),
|
||||
kind: EventKind::DidDiscard { key },
|
||||
});
|
||||
|
||||
|
|
|
@ -38,9 +38,8 @@ where
|
|||
}
|
||||
|
||||
fn report_stale_output(db: &C::DbView, key: DatabaseKeyIndex, output: DependencyIndex) {
|
||||
let runtime_id = db.runtime().id();
|
||||
db.salsa_event(Event {
|
||||
runtime_id,
|
||||
thread_id: std::thread::current().id(),
|
||||
kind: EventKind::WillDiscardStaleOutput {
|
||||
execute_key: key,
|
||||
output_key: output,
|
||||
|
|
|
@ -33,7 +33,7 @@ where
|
|||
tracing::info!("{:?}: executing query", database_key_index);
|
||||
|
||||
db.salsa_event(Event {
|
||||
runtime_id: runtime.id(),
|
||||
thread_id: std::thread::current().id(),
|
||||
kind: EventKind::WillExecute {
|
||||
database_key: database_key_index,
|
||||
},
|
||||
|
|
|
@ -150,7 +150,7 @@ impl<V> Memo<V> {
|
|||
database_key_index: DatabaseKeyIndex,
|
||||
) {
|
||||
db.salsa_event(Event {
|
||||
runtime_id: runtime.id(),
|
||||
thread_id: std::thread::current().id(),
|
||||
kind: EventKind::DidValidateMemoizedValue {
|
||||
database_key: database_key_index,
|
||||
},
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{
|
||||
hash::FxDashMap,
|
||||
key::DatabaseKeyIndex,
|
||||
runtime::{RuntimeId, WaitResult},
|
||||
Database, Id, Runtime,
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
thread::ThreadId,
|
||||
};
|
||||
|
||||
use crate::{hash::FxDashMap, key::DatabaseKeyIndex, runtime::WaitResult, Database, Id, Runtime};
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct SyncMap {
|
||||
sync_map: FxDashMap<Id, SyncState>,
|
||||
}
|
||||
|
||||
struct SyncState {
|
||||
id: RuntimeId,
|
||||
id: ThreadId,
|
||||
|
||||
/// Set to true if any other queries are blocked,
|
||||
/// waiting for this query to complete.
|
||||
|
@ -27,10 +25,11 @@ impl SyncMap {
|
|||
database_key_index: DatabaseKeyIndex,
|
||||
) -> Option<ClaimGuard<'me>> {
|
||||
let runtime = db.runtime();
|
||||
let thread_id = std::thread::current().id();
|
||||
match self.sync_map.entry(database_key_index.key_index) {
|
||||
dashmap::mapref::entry::Entry::Vacant(entry) => {
|
||||
entry.insert(SyncState {
|
||||
id: runtime.id(),
|
||||
id: thread_id,
|
||||
anyone_waiting: AtomicBool::new(false),
|
||||
});
|
||||
Some(ClaimGuard {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::{
|
||||
panic::panic_any,
|
||||
sync::{atomic::AtomicUsize, Arc},
|
||||
thread::ThreadId,
|
||||
};
|
||||
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
|
@ -24,9 +25,6 @@ use super::tracked_struct::Disambiguator;
|
|||
mod dependency_graph;
|
||||
|
||||
pub struct Runtime {
|
||||
/// Our unique runtime id.
|
||||
id: RuntimeId,
|
||||
|
||||
/// Local state that is specific to this runtime (thread).
|
||||
local_state: local_state::LocalState,
|
||||
|
||||
|
@ -64,14 +62,6 @@ pub(crate) enum WaitResult {
|
|||
Cycle(Cycle),
|
||||
}
|
||||
|
||||
/// A unique identifier for a particular runtime. Each time you create
|
||||
/// a snapshot, a fresh `RuntimeId` is generated. Once a snapshot is
|
||||
/// complete, its `RuntimeId` may potentially be re-used.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct RuntimeId {
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct StampedValue<V> {
|
||||
pub value: V,
|
||||
|
@ -101,7 +91,6 @@ impl<V> StampedValue<V> {
|
|||
impl Default for Runtime {
|
||||
fn default() -> Self {
|
||||
Runtime {
|
||||
id: RuntimeId { counter: 0 },
|
||||
local_state: Default::default(),
|
||||
revisions: (0..Durability::LEN)
|
||||
.map(|_| AtomicRevision::start())
|
||||
|
@ -117,7 +106,6 @@ impl Default for Runtime {
|
|||
impl std::fmt::Debug for Runtime {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fmt.debug_struct("Runtime")
|
||||
.field("id", &self.id)
|
||||
.field("revisions", &self.revisions)
|
||||
.field("next_id", &self.next_id)
|
||||
.field("revision_canceled", &self.revision_canceled)
|
||||
|
@ -127,10 +115,6 @@ impl std::fmt::Debug for Runtime {
|
|||
}
|
||||
|
||||
impl Runtime {
|
||||
pub(crate) fn id(&self) -> RuntimeId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub(crate) fn current_revision(&self) -> Revision {
|
||||
self.revisions[0].load()
|
||||
}
|
||||
|
@ -234,13 +218,15 @@ impl Runtime {
|
|||
/// `salsa_event` is emitted when this method is called, so that should be
|
||||
/// used instead.
|
||||
pub(crate) fn unwind_if_revision_cancelled<DB: ?Sized + Database>(&self, db: &DB) {
|
||||
let thread_id = std::thread::current().id();
|
||||
db.salsa_event(Event {
|
||||
runtime_id: self.id(),
|
||||
thread_id,
|
||||
|
||||
kind: EventKind::WillCheckCancellation,
|
||||
});
|
||||
if self.revision_canceled.load() {
|
||||
db.salsa_event(Event {
|
||||
runtime_id: self.id(),
|
||||
thread_id,
|
||||
kind: EventKind::WillCheckCancellation,
|
||||
});
|
||||
self.unwind_cancelled();
|
||||
|
@ -300,23 +286,24 @@ impl Runtime {
|
|||
&self,
|
||||
db: &dyn Database,
|
||||
database_key: DatabaseKeyIndex,
|
||||
other_id: RuntimeId,
|
||||
other_id: ThreadId,
|
||||
query_mutex_guard: QueryMutexGuard,
|
||||
) {
|
||||
let mut dg = self.dependency_graph.lock();
|
||||
let thread_id = std::thread::current().id();
|
||||
|
||||
if dg.depends_on(other_id, self.id()) {
|
||||
if dg.depends_on(other_id, thread_id) {
|
||||
self.unblock_cycle_and_maybe_throw(db, &mut dg, database_key, other_id);
|
||||
|
||||
// If the above fn returns, then (via cycle recovery) it has unblocked the
|
||||
// cycle, so we can continue.
|
||||
assert!(!dg.depends_on(other_id, self.id()));
|
||||
assert!(!dg.depends_on(other_id, thread_id));
|
||||
}
|
||||
|
||||
db.salsa_event(Event {
|
||||
runtime_id: self.id(),
|
||||
thread_id,
|
||||
kind: EventKind::WillBlockOn {
|
||||
other_runtime_id: other_id,
|
||||
other_thread_id: other_id,
|
||||
database_key,
|
||||
},
|
||||
});
|
||||
|
@ -325,7 +312,7 @@ impl Runtime {
|
|||
|
||||
let (stack, result) = DependencyGraph::block_on(
|
||||
dg,
|
||||
self.id(),
|
||||
thread_id,
|
||||
database_key,
|
||||
other_id,
|
||||
stack,
|
||||
|
@ -359,7 +346,7 @@ impl Runtime {
|
|||
db: &dyn Database,
|
||||
dg: &mut DependencyGraph,
|
||||
database_key_index: DatabaseKeyIndex,
|
||||
to_id: RuntimeId,
|
||||
to_id: ThreadId,
|
||||
) {
|
||||
tracing::debug!(
|
||||
"unblock_cycle_and_maybe_throw(database_key={:?})",
|
||||
|
@ -367,7 +354,7 @@ impl Runtime {
|
|||
);
|
||||
|
||||
let mut from_stack = self.local_state.take_query_stack();
|
||||
let from_id = self.id();
|
||||
let from_id = std::thread::current().id();
|
||||
|
||||
// Make a "dummy stack frame". As we iterate through the cycle, we will collect the
|
||||
// inputs from each participant. Then, if we are participating in cycle recovery, we
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use std::sync::Arc;
|
||||
use std::thread::ThreadId;
|
||||
|
||||
use crate::active_query::ActiveQuery;
|
||||
use crate::key::DatabaseKeyIndex;
|
||||
use crate::runtime::{RuntimeId, WaitResult};
|
||||
use crate::runtime::WaitResult;
|
||||
use parking_lot::{Condvar, MutexGuard};
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
|
@ -15,21 +16,21 @@ pub(super) struct DependencyGraph {
|
|||
/// `K` is blocked on some query executing in the runtime `V`.
|
||||
/// This encodes a graph that must be acyclic (or else deadlock
|
||||
/// will result).
|
||||
edges: FxHashMap<RuntimeId, Edge>,
|
||||
edges: FxHashMap<ThreadId, Edge>,
|
||||
|
||||
/// Encodes the `RuntimeId` that are blocked waiting for the result
|
||||
/// Encodes the `ThreadId` that are blocked waiting for the result
|
||||
/// of a given query.
|
||||
query_dependents: FxHashMap<DatabaseKeyIndex, SmallVec<[RuntimeId; 4]>>,
|
||||
query_dependents: FxHashMap<DatabaseKeyIndex, SmallVec<[ThreadId; 4]>>,
|
||||
|
||||
/// When a key K completes which had dependent queries Qs blocked on it,
|
||||
/// it stores its `WaitResult` here. As they wake up, each query Q in Qs will
|
||||
/// come here to fetch their results.
|
||||
wait_results: FxHashMap<RuntimeId, (QueryStack, WaitResult)>,
|
||||
wait_results: FxHashMap<ThreadId, (QueryStack, WaitResult)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Edge {
|
||||
blocked_on_id: RuntimeId,
|
||||
blocked_on_id: ThreadId,
|
||||
blocked_on_key: DatabaseKeyIndex,
|
||||
stack: QueryStack,
|
||||
|
||||
|
@ -42,7 +43,7 @@ impl DependencyGraph {
|
|||
/// True if `from_id` depends on `to_id`.
|
||||
///
|
||||
/// (i.e., there is a path from `from_id` to `to_id` in the graph.)
|
||||
pub(super) fn depends_on(&mut self, from_id: RuntimeId, to_id: RuntimeId) -> bool {
|
||||
pub(super) fn depends_on(&mut self, from_id: ThreadId, to_id: ThreadId) -> bool {
|
||||
let mut p = from_id;
|
||||
while let Some(q) = self.edges.get(&p).map(|edge| edge.blocked_on_id) {
|
||||
if q == to_id {
|
||||
|
@ -62,10 +63,10 @@ impl DependencyGraph {
|
|||
/// 3. ...and `to_id` is transitively dependent on something which is present on `from_stack`.
|
||||
pub(super) fn for_each_cycle_participant(
|
||||
&mut self,
|
||||
from_id: RuntimeId,
|
||||
from_id: ThreadId,
|
||||
from_stack: &mut QueryStack,
|
||||
database_key: DatabaseKeyIndex,
|
||||
to_id: RuntimeId,
|
||||
to_id: ThreadId,
|
||||
mut closure: impl FnMut(&mut [ActiveQuery]),
|
||||
) {
|
||||
debug_assert!(self.depends_on(to_id, from_id));
|
||||
|
@ -130,10 +131,10 @@ impl DependencyGraph {
|
|||
/// * Others is true if other runtimes were unblocked.
|
||||
pub(super) fn maybe_unblock_runtimes_in_cycle(
|
||||
&mut self,
|
||||
from_id: RuntimeId,
|
||||
from_id: ThreadId,
|
||||
from_stack: &QueryStack,
|
||||
database_key: DatabaseKeyIndex,
|
||||
to_id: RuntimeId,
|
||||
to_id: ThreadId,
|
||||
) -> (bool, bool) {
|
||||
// See diagram in `for_each_cycle_participant`.
|
||||
let mut id = to_id;
|
||||
|
@ -194,9 +195,9 @@ impl DependencyGraph {
|
|||
/// * `held_mutex` is a read lock (or stronger) on `database_key`
|
||||
pub(super) fn block_on<QueryMutexGuard>(
|
||||
mut me: MutexGuard<'_, Self>,
|
||||
from_id: RuntimeId,
|
||||
from_id: ThreadId,
|
||||
database_key: DatabaseKeyIndex,
|
||||
to_id: RuntimeId,
|
||||
to_id: ThreadId,
|
||||
from_stack: QueryStack,
|
||||
query_mutex_guard: QueryMutexGuard,
|
||||
) -> (QueryStack, WaitResult) {
|
||||
|
@ -220,9 +221,9 @@ impl DependencyGraph {
|
|||
/// computing `database_key`.
|
||||
fn add_edge(
|
||||
&mut self,
|
||||
from_id: RuntimeId,
|
||||
from_id: ThreadId,
|
||||
database_key: DatabaseKeyIndex,
|
||||
to_id: RuntimeId,
|
||||
to_id: ThreadId,
|
||||
from_stack: QueryStack,
|
||||
) -> Arc<parking_lot::Condvar> {
|
||||
assert_ne!(from_id, to_id);
|
||||
|
@ -266,7 +267,7 @@ impl DependencyGraph {
|
|||
/// Unblock the runtime with the given id with the given wait-result.
|
||||
/// This will cause it resume execution (though it will have to grab
|
||||
/// the lock on this data structure first, to recover the wait result).
|
||||
fn unblock_runtime(&mut self, id: RuntimeId, wait_result: WaitResult) {
|
||||
fn unblock_runtime(&mut self, id: ThreadId, wait_result: WaitResult) {
|
||||
let edge = self.edges.remove(&id).expect("not blocked");
|
||||
self.wait_results.insert(id, (edge.stack, wait_result));
|
||||
|
||||
|
|
|
@ -391,7 +391,7 @@ where
|
|||
/// discussion and important considerations.
|
||||
pub(crate) fn delete_entity(&self, db: &dyn crate::Database, id: Id) {
|
||||
db.salsa_event(Event {
|
||||
runtime_id: db.runtime().id(),
|
||||
thread_id: std::thread::current().id(),
|
||||
kind: crate::EventKind::DidDiscard {
|
||||
key: self.database_key_index(id),
|
||||
},
|
||||
|
|
|
@ -76,8 +76,8 @@ fn test_leaked_inputs_ignored() {
|
|||
let result_in_rev_1 = function(&db, input);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: function(0) } }",
|
||||
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
|
||||
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(0) } }",
|
||||
]"#]]);
|
||||
|
||||
assert_eq!(result_in_rev_1, 0);
|
||||
|
@ -92,8 +92,8 @@ fn test_leaked_inputs_ignored() {
|
|||
let result_in_rev_2 = function(&db, input);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: function(0) } }",
|
||||
"Event { thread_id: ThreadId(2), kind: WillCheckCancellation }",
|
||||
"Event { thread_id: ThreadId(2), kind: WillExecute { database_key: function(0) } }",
|
||||
]"#]]);
|
||||
|
||||
// Because salsa did not see any way for the tracked
|
||||
|
|
|
@ -1,370 +0,0 @@
|
|||
//! Test that a `tracked` fn on a `salsa::input`
|
||||
//! compiles and executes successfully.
|
||||
|
||||
use expect_test::expect;
|
||||
mod common;
|
||||
use common::{HasLogger, Logger};
|
||||
use salsa::Setter;
|
||||
use test_log::test;
|
||||
|
||||
#[salsa::db]
|
||||
trait Db: salsa::Database + HasLogger {}
|
||||
|
||||
#[salsa::input]
|
||||
struct MyInput {
|
||||
field: u32,
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
struct MyTracked<'db> {
|
||||
input: MyInput,
|
||||
}
|
||||
|
||||
/// If the input is in the range 0..10, this is specified to return 10.
|
||||
/// Otherwise, the default occurs, and it returns the input.
|
||||
#[salsa::tracked(specify)]
|
||||
fn maybe_specified<'db>(db: &'db dyn Db, tracked: MyTracked<'db>) -> u32 {
|
||||
db.push_log(format!("maybe_specified({:?})", tracked));
|
||||
tracked.input(db).field(db)
|
||||
}
|
||||
|
||||
/// Reads maybe-specified and multiplies it by 10.
|
||||
/// This is here to show whether we can detect when `maybe_specified` has changed
|
||||
/// and control down-stream work accordingly.
|
||||
#[salsa::tracked]
|
||||
fn read_maybe_specified<'db>(db: &'db dyn Db, tracked: MyTracked<'db>) -> u32 {
|
||||
db.push_log(format!("read_maybe_specified({:?})", tracked));
|
||||
maybe_specified(db, tracked) * 10
|
||||
}
|
||||
|
||||
/// Create a tracked value and *maybe* specify a value for
|
||||
/// `maybe_specified`
|
||||
#[salsa::tracked]
|
||||
fn create_tracked(db: &dyn Db, input: MyInput) -> MyTracked<'_> {
|
||||
db.push_log(format!("create_tracked({:?})", input));
|
||||
let tracked = MyTracked::new(db, input);
|
||||
if input.field(db) < 10 {
|
||||
maybe_specified::specify(db, tracked, 10);
|
||||
}
|
||||
tracked
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
fn final_result(db: &dyn Db, input: MyInput) -> u32 {
|
||||
db.push_log(format!("final_result({:?})", input));
|
||||
let tracked = create_tracked(db, input);
|
||||
read_maybe_specified(db, tracked)
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
#[derive(Default)]
|
||||
struct Database {
|
||||
storage: salsa::Storage<Self>,
|
||||
logger: Logger,
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl salsa::Database for Database {
|
||||
fn salsa_event(&self, event: salsa::Event) {
|
||||
self.push_log(format!("{event:?}"));
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
impl Db for Database {}
|
||||
|
||||
impl HasLogger for Database {
|
||||
fn logger(&self) -> &Logger {
|
||||
&self.logger
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_0() {
|
||||
let mut db = Database::default();
|
||||
|
||||
let input = MyInput::new(&db, 0);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 0 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 0 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 0 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
]"#]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_5() {
|
||||
let mut db = Database::default();
|
||||
|
||||
let input = MyInput::new(&db, 5);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 5 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 5 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 5 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
]"#]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_10() {
|
||||
let mut db = Database::default();
|
||||
|
||||
let input = MyInput::new(&db, 10);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 10 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 10 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 10 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: maybe_specified(0) } }",
|
||||
"maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 10 } })",
|
||||
]"#]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_20() {
|
||||
let mut db = Database::default();
|
||||
|
||||
let input = MyInput::new(&db, 20);
|
||||
assert_eq!(final_result(&db, input), 200);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 20 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 20 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 20 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: maybe_specified(0) } }",
|
||||
"maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 20 } })",
|
||||
]"#]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_0_then_5_then_20() {
|
||||
let mut db = Database::default();
|
||||
|
||||
// Set input to 0:
|
||||
//
|
||||
// * `create_tracked` specifies `10` for `maybe_specified`
|
||||
// * final resuilt of `100` is derived by executing `read_maybe_specified`
|
||||
let input = MyInput::new(&db, 0);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 0 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 0 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 0 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
]"#]]);
|
||||
|
||||
// Set input to 5:
|
||||
//
|
||||
// * `create_tracked` does re-execute, but specifies same value for `maybe_specified` as before
|
||||
// * `read_maybe_specified` does not re-execute (its input has not changed)
|
||||
input.set_field(&mut db).to(5);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 5 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: read_maybe_specified(0) } }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: final_result(0) } }",
|
||||
]"#]]);
|
||||
|
||||
// Set input to 20:
|
||||
//
|
||||
// * `create_tracked` re-executes but does not specify any value
|
||||
// * `read_maybe_specified` is invoked and it calls `maybe_specified`, which now executes
|
||||
// (its value has not been specified)
|
||||
input.set_field(&mut db).to(20);
|
||||
assert_eq!(final_result(&db, input), 200);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 20 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillDiscardStaleOutput { execute_key: create_tracked(0), output_key: maybe_specified(0) } }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: maybe_specified(0) } }",
|
||||
"maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 20 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 20 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 20 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
]"#]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_0_then_5_then_10_then_20() {
|
||||
let mut db = Database::default();
|
||||
|
||||
// Set input to 0:
|
||||
//
|
||||
// * `create_tracked` specifies `10` for `maybe_specified`
|
||||
// * final resuilt of `100` is derived by executing `read_maybe_specified`
|
||||
let input = MyInput::new(&db, 0);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 0 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 0 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 0 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
]"#]]);
|
||||
|
||||
// Set input to 5:
|
||||
//
|
||||
// * `create_tracked` does re-execute, but specifies same value for `maybe_specified` as before
|
||||
// * `read_maybe_specified` does not re-execute (its input has not changed)
|
||||
input.set_field(&mut db).to(5);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 5 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: read_maybe_specified(0) } }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: final_result(0) } }",
|
||||
]"#]]);
|
||||
|
||||
// Set input to 10:
|
||||
//
|
||||
// * `create_tracked` does re-execute and specifies no value for `maybe_specified`
|
||||
// * `maybe_specified_value` returns 10; this is the same value as was specified.
|
||||
// * `read_maybe_specified` therefore does NOT need to execute.
|
||||
input.set_field(&mut db).to(10);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 10 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillDiscardStaleOutput { execute_key: create_tracked(0), output_key: maybe_specified(0) } }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: maybe_specified(0) } }",
|
||||
"maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 10 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: read_maybe_specified(0) } }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: DidValidateMemoizedValue { database_key: final_result(0) } }",
|
||||
]"#]]);
|
||||
|
||||
// Set input to 20:
|
||||
//
|
||||
// * Everything re-executes to get new result (200).
|
||||
input.set_field(&mut db).to(20);
|
||||
assert_eq!(final_result(&db, input), 200);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 20 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: maybe_specified(0) } }",
|
||||
"maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 20 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 20 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 20 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
]"#]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_5_then_20() {
|
||||
let mut db = Database::default();
|
||||
|
||||
let input = MyInput::new(&db, 5);
|
||||
assert_eq!(final_result(&db, input), 100);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 5 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 5 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 5 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
]"#]]);
|
||||
|
||||
input.set_field(&mut db).to(20);
|
||||
assert_eq!(final_result(&db, input), 200);
|
||||
db.assert_logs(expect![[r#"
|
||||
[
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: create_tracked(0) } }",
|
||||
"create_tracked(MyInput { [salsa id]: Id(0), field: 20 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillDiscardStaleOutput { execute_key: create_tracked(0), output_key: maybe_specified(0) } }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: maybe_specified(0) } }",
|
||||
"maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 20 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: read_maybe_specified(0) } }",
|
||||
"read_maybe_specified(MyTracked { [salsa id]: Id(0), input: MyInput { [salsa id]: Id(0), field: 20 } })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillExecute { database_key: final_result(0) } }",
|
||||
"final_result(MyInput { [salsa id]: Id(0), field: 20 })",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
"Event { runtime_id: RuntimeId { counter: 0 }, kind: WillCheckCancellation }",
|
||||
]"#]]);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue