Skip to content

Commit ca187bf

Browse files
committed
record durabilities on interned values
1 parent 6505453 commit ca187bf

File tree

4 files changed

+117
-50
lines changed

4 files changed

+117
-50
lines changed

src/durability.rs

+10
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ impl Durability {
4747
pub(crate) fn index(self) -> usize {
4848
self.0 as usize
4949
}
50+
51+
pub(crate) fn as_u8(self) -> u8 {
52+
self.0
53+
}
54+
55+
pub(crate) fn from_u8(value: u8) -> Self {
56+
assert!((value as usize) < Self::LEN, "invalid durability");
57+
58+
Self(value)
59+
}
5060
}
5161

5262
impl Default for Durability {

src/interned.rs

+63-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use std::fmt;
1313
use std::hash::{BuildHasher, Hash, Hasher};
1414
use std::marker::PhantomData;
1515
use std::path::{Path, PathBuf};
16+
use std::sync::atomic::{AtomicU8, Ordering};
1617

1718
use super::hash::FxDashMap;
1819
use super::ingredient::Ingredient;
@@ -68,10 +69,27 @@ where
6869
data: C::Data<'static>,
6970
memos: MemoTable,
7071
syncs: SyncTable,
72+
7173
/// The revision the value was first interned in.
7274
first_interned_at: Revision,
75+
7376
/// The most recent interned revision.
7477
last_interned_at: AtomicRevision,
78+
79+
/// The minimum durability of all inputs consumed by the creator
80+
/// query prior to creating this tracked struct. If any of those
81+
/// inputs changes, then the creator query may create this struct
82+
/// with different values.
83+
durability: AtomicU8,
84+
}
85+
86+
impl<C> Value<C>
87+
where
88+
C: Configuration,
89+
{
90+
fn durability(&self) -> Durability {
91+
Durability::from_u8(self.durability.load(Ordering::Acquire))
92+
}
7593
}
7694

7795
impl<C: Configuration> Default for JarImpl<C> {
@@ -148,13 +166,25 @@ where
148166
// SAFETY: Read lock on map is held during this block
149167
let id = unsafe { *bucket.as_ref().1.get() };
150168

151-
// Sync the value's revision.
152169
let value = zalsa.table().get::<Value<C>>(id);
170+
171+
// Sync the value's revision.
153172
value.last_interned_at.store(current_revision);
154173

174+
let durability = if let Some((_, stamp)) = zalsa_local.active_query() {
175+
// Record the maximum durability across all queries that intern this value.
176+
let previous_durability = value
177+
.durability
178+
.fetch_max(stamp.durability.as_u8(), Ordering::AcqRel);
179+
180+
Durability::from_u8(previous_durability).max(stamp.durability)
181+
} else {
182+
value.durability()
183+
};
184+
155185
// Record a dependency on this value.
156186
let index = self.database_key_index(id);
157-
zalsa_local.report_tracked_read(index, Durability::MAX, current_revision);
187+
zalsa_local.report_tracked_read(index, durability, current_revision);
158188

159189
return C::struct_from_id(id);
160190
}
@@ -170,13 +200,25 @@ where
170200
let id = *entry.get();
171201
drop(entry);
172202

173-
// Sync the value's revision.
174203
let value = zalsa.table().get::<Value<C>>(id);
204+
205+
// Sync the value's revision.
175206
value.last_interned_at.store(current_revision);
176207

208+
let durability = if let Some((_, stamp)) = zalsa_local.active_query() {
209+
// Record the maximum durability across all queries that intern this value.
210+
let previous_durability = value
211+
.durability
212+
.fetch_max(stamp.durability.as_u8(), Ordering::AcqRel);
213+
214+
Durability::from_u8(previous_durability).max(stamp.durability)
215+
} else {
216+
value.durability()
217+
};
218+
177219
// Record a dependency on this value.
178220
let index = self.database_key_index(id);
179-
zalsa_local.report_tracked_read(index, Durability::MAX, current_revision);
221+
zalsa_local.report_tracked_read(index, durability, current_revision);
180222

181223
C::struct_from_id(id)
182224
}
@@ -186,18 +228,30 @@ where
186228
let zalsa = db.zalsa();
187229
let table = zalsa.table();
188230

231+
let (durability, last_interned_at) = match zalsa_local.active_query() {
232+
// Record the durability of the current query, along with the revision
233+
// we are interning in.
234+
Some((_, stamp)) => (stamp.durability, current_revision),
235+
236+
// An interned value created outside of a query is considered immortal.
237+
// The durability in this case doesn't really matter.
238+
None => (Durability::MAX, Revision::from(usize::MAX)),
239+
};
240+
189241
let next_id = zalsa_local.allocate(table, self.ingredient_index, || Value::<C> {
190242
data: internal_data,
191243
memos: Default::default(),
192244
syncs: Default::default(),
193245
first_interned_at: current_revision,
194-
last_interned_at: AtomicRevision::from(current_revision),
246+
durability: AtomicU8::new(durability.as_u8()),
247+
last_interned_at: AtomicRevision::from(last_interned_at),
195248
});
249+
196250
entry.insert(next_id);
197251

198252
// Record a dependency on this value.
199253
let index = self.database_key_index(next_id);
200-
zalsa_local.report_tracked_read(index, Durability::MAX, current_revision);
254+
zalsa_local.report_tracked_read(index, durability, current_revision);
201255

202256
C::struct_from_id(next_id)
203257
}
@@ -217,9 +271,10 @@ where
217271
/// to the interned item.
218272
pub fn data<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Data<'db> {
219273
let value = db.zalsa().table().get::<Value<C>>(id);
274+
let last_changed_revision = db.zalsa().last_changed_revision(value.durability());
220275
assert!(
221-
value.last_interned_at.load() >= db.zalsa().current_revision(),
222-
"Data was not interned in the current revision."
276+
value.last_interned_at.load() >= last_changed_revision,
277+
"Data was not interned in the latest revision for its durability."
223278
);
224279
unsafe { Self::from_internal_data(&value.data) }
225280
}

src/tracked_struct.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,10 @@ pub struct Value<C>
182182
where
183183
C: Configuration,
184184
{
185-
/// The durability minimum durability of all inputs consumed
186-
/// by the creator query prior to creating this tracked struct.
187-
/// If any of those inputs changes, then the creator query may
188-
/// create this struct with different values.
185+
/// The minimum durability of all inputs consumed by the creator
186+
/// query prior to creating this tracked struct. If any of those
187+
/// inputs changes, then the creator query may create this struct
188+
/// with different values.
189189
durability: Durability,
190190

191191
/// The revision when this tracked struct was last updated.
@@ -283,7 +283,6 @@ where
283283

284284
let identity = Identity {
285285
identity_hash,
286-
287286
disambiguator,
288287
};
289288

tests/interned-revisions.rs

+40-37
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
//! compiles and executes successfully.
33
44
mod common;
5+
use common::LogDatabase;
6+
use expect_test::expect;
57
use salsa::{Database, Setter};
68
use test_log::test;
79

@@ -16,7 +18,7 @@ struct Interned<'db> {
1618
}
1719

1820
#[test]
19-
fn test_shallow_memo() {
21+
fn test_durability() {
2022
#[salsa::tracked]
2123
fn function<'db>(db: &'db dyn Database, _input: Input) -> Interned<'db> {
2224
Interned::new(db, 0)
@@ -34,39 +36,40 @@ fn test_shallow_memo() {
3436
assert_eq!(result_in_rev_2.field1(&db), 0);
3537
}
3638

37-
// #[test]
38-
// fn test_no_reintern_input() {
39-
// #[salsa::tracked]
40-
// fn function<'db>(db: &'db dyn Database, input: Input) -> Interned<'db> {
41-
// function2(db, input.field1(db))
42-
// }
43-
//
44-
// fn function2<'db>(db: &'db dyn Database, value: usize) -> Interned<'db> {
45-
// Interned::new(db, value)
46-
// }
47-
//
48-
// let mut db = common::EventLoggerDatabase::default();
49-
//
50-
// let input = Input::new(&db, 0);
51-
// let result_in_rev_1 = function(&db, input);
52-
// db.assert_logs(expect![[r#"
53-
// [
54-
// "Event { thread_id: ThreadId(3), kind: WillCheckCancellation }",
55-
// "Event { thread_id: ThreadId(3), kind: WillExecute { database_key: function(Id(0)) } }",
56-
// ]"#]]);
57-
//
58-
// assert_eq!(result_in_rev_1.field1(&db), 0);
59-
//
60-
// // Modify the input to force the value to be re-interned.
61-
// input.set_field1(&mut db).to(1);
62-
//
63-
// let result_in_rev_2 = function(&db, input);
64-
// db.assert_logs(expect![[r#"
65-
// [
66-
// "Event { thread_id: ThreadId(3), kind: DidSetCancellationFlag }",
67-
// "Event { thread_id: ThreadId(3), kind: WillCheckCancellation }",
68-
// "Event { thread_id: ThreadId(3), kind: WillExecute { database_key: function(Id(0)) } }",
69-
// ]"#]]);
70-
//
71-
// assert_eq!(result_in_rev_2.field1(&db), 1);
72-
// }
39+
#[test]
40+
fn test_durability2() {
41+
#[salsa::tracked]
42+
fn function<'db>(db: &'db dyn Database, input: Input) -> Interned<'db> {
43+
let _ = input.field1(db);
44+
function2(db)
45+
}
46+
47+
fn function2<'db>(db: &'db dyn Database) -> Interned<'db> {
48+
Interned::new(db, 0)
49+
}
50+
51+
let mut db = common::EventLoggerDatabase::default();
52+
53+
let input = Input::new(&db, 0);
54+
let result_in_rev_1 = function(&db, input);
55+
db.assert_logs(expect![[r#"
56+
[
57+
"Event { thread_id: ThreadId(3), kind: WillCheckCancellation }",
58+
"Event { thread_id: ThreadId(3), kind: WillExecute { database_key: function(Id(0)) } }",
59+
]"#]]);
60+
61+
assert_eq!(result_in_rev_1.field1(&db), 0);
62+
63+
// Modify the input to force the value to be re-interned.
64+
input.set_field1(&mut db).to(1);
65+
66+
let result_in_rev_2 = function(&db, input);
67+
db.assert_logs(expect![[r#"
68+
[
69+
"Event { thread_id: ThreadId(3), kind: DidSetCancellationFlag }",
70+
"Event { thread_id: ThreadId(3), kind: WillCheckCancellation }",
71+
"Event { thread_id: ThreadId(3), kind: WillExecute { database_key: function(Id(0)) } }",
72+
]"#]]);
73+
74+
assert_eq!(result_in_rev_2.field1(&db), 0);
75+
}

0 commit comments

Comments
 (0)