Skip to content

Commit 7bdf587

Browse files
committed
record durabilities on interned values
1 parent 6196c7f commit 7bdf587

File tree

3 files changed

+83
-14
lines changed

3 files changed

+83
-14
lines changed

src/durability.rs

+19
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ enum DurabilityVal {
3535
High = 2,
3636
}
3737

38+
impl From<u8> for DurabilityVal {
39+
fn from(value: u8) -> Self {
40+
match value {
41+
0 => DurabilityVal::Low,
42+
1 => DurabilityVal::Medium,
43+
2 => DurabilityVal::High,
44+
_ => panic!("invalid durability"),
45+
}
46+
}
47+
}
48+
3849
impl Durability {
3950
/// Low durability: things that change frequently.
4051
///
@@ -68,6 +79,14 @@ impl Durability {
6879
pub(crate) fn index(self) -> usize {
6980
self.0 as usize
7081
}
82+
83+
pub(crate) fn as_u8(self) -> u8 {
84+
self.0 as u8
85+
}
86+
87+
pub(crate) fn from_u8(value: u8) -> Self {
88+
Self(DurabilityVal::from(value))
89+
}
7190
}
7291

7392
impl Default for Durability {

src/interned.rs

+60-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::fmt;
1616
use std::hash::{BuildHasher, Hash, Hasher};
1717
use std::marker::PhantomData;
1818
use std::path::{Path, PathBuf};
19+
use std::sync::atomic::{AtomicU8, Ordering};
1920

2021
use super::hash::FxDashMap;
2122
use super::ingredient::Ingredient;
@@ -71,16 +72,29 @@ where
7172
fields: C::Fields<'static>,
7273
memos: MemoTable,
7374
syncs: SyncTable,
75+
7476
/// The revision the value was first interned in.
7577
first_interned_at: Revision,
78+
7679
/// The most recent interned revision.
7780
last_interned_at: AtomicRevision,
81+
82+
/// The minimum durability of all inputs consumed by the creator
83+
/// query prior to creating this tracked struct. If any of those
84+
/// inputs changes, then the creator query may create this struct
85+
/// with different values.
86+
durability: AtomicU8,
7887
}
7988

8089
impl<C> Value<C>
8190
where
8291
C: Configuration,
8392
{
93+
// Loads the durability of this interned struct.
94+
fn durability(&self) -> Durability {
95+
Durability::from_u8(self.durability.load(Ordering::Acquire))
96+
}
97+
8498
/// Fields of this interned struct.
8599
#[cfg(feature = "salsa_unstable")]
86100
pub fn fields(&self) -> &C::Fields<'static> {
@@ -193,15 +207,27 @@ where
193207
// SAFETY: Read lock on map is held during this block
194208
let id = unsafe { *bucket.as_ref().1.get() };
195209

196-
// Sync the value's revision.
197210
let value = zalsa.table().get::<Value<C>>(id);
211+
212+
// Sync the value's revision.
198213
value.last_interned_at.store(current_revision);
199214

215+
let durability = if let Some((_, stamp)) = zalsa_local.active_query() {
216+
// Record the maximum durability across all queries that intern this value.
217+
let previous_durability = value
218+
.durability
219+
.fetch_max(stamp.durability.as_u8(), Ordering::AcqRel);
220+
221+
Durability::from_u8(previous_durability).max(stamp.durability)
222+
} else {
223+
value.durability()
224+
};
225+
200226
// Record a dependency on this value.
201227
let index = self.database_key_index(id);
202228
zalsa_local.report_tracked_read(
203229
index,
204-
Durability::MAX,
230+
durability,
205231
current_revision,
206232
InputAccumulatedValues::Empty,
207233
);
@@ -217,35 +243,56 @@ where
217243
// Data has been interned by a racing call, use that ID instead
218244
Ok(slot) => {
219245
let id = unsafe { *slot.as_ref().1.get() };
220-
drop(slot);
246+
let value = zalsa.table().get::<Value<C>>(id);
221247

222248
// Sync the value's revision.
223-
let value = zalsa.table().get::<Value<C>>(id);
224249
value.last_interned_at.store(current_revision);
225250

251+
let durability = if let Some((_, stamp)) = zalsa_local.active_query() {
252+
// Record the maximum durability across all queries that intern this value.
253+
let previous_durability = value
254+
.durability
255+
.fetch_max(stamp.durability.as_u8(), Ordering::AcqRel);
256+
257+
Durability::from_u8(previous_durability).max(stamp.durability)
258+
} else {
259+
value.durability()
260+
};
261+
226262
// Record a dependency on this value.
227263
let index = self.database_key_index(id);
228264
zalsa_local.report_tracked_read(
229265
index,
230-
Durability::MAX,
266+
durability,
231267
current_revision,
232268
InputAccumulatedValues::Empty,
233269
);
234270

235-
return id;
271+
id
236272
}
237273

238274
// We won any races so should intern the data
239275
Err(slot) => {
240276
let zalsa = db.zalsa();
241277
let table = zalsa.table();
242278

279+
let (durability, last_interned_at) = match zalsa_local.active_query() {
280+
// Record the durability of the current query, along with the revision
281+
// we are interning in.
282+
Some((_, stamp)) => (stamp.durability, current_revision),
283+
284+
// An interned value created outside of a query is considered immortal.
285+
// The durability in this case doesn't really matter.
286+
None => (Durability::MAX, Revision::from(usize::MAX)),
287+
};
288+
243289
let id = zalsa_local.allocate(table, self.ingredient_index, |id| Value::<C> {
244290
fields: unsafe { self.to_internal_data(assemble(id, key)) },
245291
memos: Default::default(),
246292
syncs: Default::default(),
293+
durability: AtomicU8::new(durability.as_u8()),
247294
first_interned_at: current_revision,
248-
last_interned_at: AtomicRevision::from(current_revision),
295+
last_interned_at: AtomicRevision::from(last_interned_at),
249296
});
250297

251298
let value = (
@@ -266,7 +313,7 @@ where
266313
let index = self.database_key_index(id);
267314
zalsa_local.report_tracked_read(
268315
index,
269-
Durability::MAX,
316+
durability,
270317
current_revision,
271318
InputAccumulatedValues::Empty,
272319
);
@@ -286,10 +333,13 @@ where
286333
/// to the interned item.
287334
pub fn data<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Fields<'db> {
288335
let internal_data = db.zalsa().table().get::<Value<C>>(id);
336+
let last_changed_revision = db.zalsa().last_changed_revision(internal_data.durability());
337+
289338
assert!(
290-
internal_data.last_interned_at.load() >= db.zalsa().current_revision(),
291-
"Data was not interned in the current revision."
339+
internal_data.last_interned_at.load() >= last_changed_revision,
340+
"Data was not interned in the latest revision for its durability."
292341
);
342+
293343
unsafe { Self::from_internal_data(&internal_data.fields) }
294344
}
295345

src/tracked_struct.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,10 @@ pub struct Value<C>
255255
where
256256
C: Configuration,
257257
{
258-
/// The durability minimum durability of all inputs consumed
259-
/// by the creator query prior to creating this tracked struct.
260-
/// If any of those inputs changes, then the creator query may
261-
/// create this struct with different values.
258+
/// The minimum durability of all inputs consumed by the creator
259+
/// query prior to creating this tracked struct. If any of those
260+
/// inputs changes, then the creator query may create this struct
261+
/// with different values.
262262
durability: Durability,
263263

264264
/// The revision when this tracked struct was last updated.

0 commit comments

Comments
 (0)