@@ -16,6 +16,7 @@ use std::fmt;
16
16
use std:: hash:: { BuildHasher , Hash , Hasher } ;
17
17
use std:: marker:: PhantomData ;
18
18
use std:: path:: { Path , PathBuf } ;
19
+ use std:: sync:: atomic:: { AtomicU8 , Ordering } ;
19
20
20
21
use super :: hash:: FxDashMap ;
21
22
use super :: ingredient:: Ingredient ;
@@ -71,16 +72,29 @@ where
71
72
fields : C :: Fields < ' static > ,
72
73
memos : MemoTable ,
73
74
syncs : SyncTable ,
75
+
74
76
/// The revision the value was first interned in.
75
77
first_interned_at : Revision ,
78
+
76
79
/// The most recent interned revision.
77
80
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 ,
78
87
}
79
88
80
89
impl < C > Value < C >
81
90
where
82
91
C : Configuration ,
83
92
{
93
+ // Loads the durability of this interned struct.
94
+ fn durability ( & self ) -> Durability {
95
+ Durability :: from_u8 ( self . durability . load ( Ordering :: Acquire ) )
96
+ }
97
+
84
98
/// Fields of this interned struct.
85
99
#[ cfg( feature = "salsa_unstable" ) ]
86
100
pub fn fields ( & self ) -> & C :: Fields < ' static > {
@@ -193,15 +207,27 @@ where
193
207
// SAFETY: Read lock on map is held during this block
194
208
let id = unsafe { * bucket. as_ref ( ) . 1 . get ( ) } ;
195
209
196
- // Sync the value's revision.
197
210
let value = zalsa. table ( ) . get :: < Value < C > > ( id) ;
211
+
212
+ // Sync the value's revision.
198
213
value. last_interned_at . store ( current_revision) ;
199
214
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
+
200
226
// Record a dependency on this value.
201
227
let index = self . database_key_index ( id) ;
202
228
zalsa_local. report_tracked_read (
203
229
index,
204
- Durability :: MAX ,
230
+ durability ,
205
231
current_revision,
206
232
InputAccumulatedValues :: Empty ,
207
233
) ;
@@ -217,35 +243,56 @@ where
217
243
// Data has been interned by a racing call, use that ID instead
218
244
Ok ( slot) => {
219
245
let id = unsafe { * slot. as_ref ( ) . 1 . get ( ) } ;
220
- drop ( slot ) ;
246
+ let value = zalsa . table ( ) . get :: < Value < C > > ( id ) ;
221
247
222
248
// Sync the value's revision.
223
- let value = zalsa. table ( ) . get :: < Value < C > > ( id) ;
224
249
value. last_interned_at . store ( current_revision) ;
225
250
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
+
226
262
// Record a dependency on this value.
227
263
let index = self . database_key_index ( id) ;
228
264
zalsa_local. report_tracked_read (
229
265
index,
230
- Durability :: MAX ,
266
+ durability ,
231
267
current_revision,
232
268
InputAccumulatedValues :: Empty ,
233
269
) ;
234
270
235
- return id ;
271
+ id
236
272
}
237
273
238
274
// We won any races so should intern the data
239
275
Err ( slot) => {
240
276
let zalsa = db. zalsa ( ) ;
241
277
let table = zalsa. table ( ) ;
242
278
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
+
243
289
let id = zalsa_local. allocate ( table, self . ingredient_index , |id| Value :: < C > {
244
290
fields : unsafe { self . to_internal_data ( assemble ( id, key) ) } ,
245
291
memos : Default :: default ( ) ,
246
292
syncs : Default :: default ( ) ,
293
+ durability : AtomicU8 :: new ( durability. as_u8 ( ) ) ,
247
294
first_interned_at : current_revision,
248
- last_interned_at : AtomicRevision :: from ( current_revision ) ,
295
+ last_interned_at : AtomicRevision :: from ( last_interned_at ) ,
249
296
} ) ;
250
297
251
298
let value = (
@@ -266,7 +313,7 @@ where
266
313
let index = self . database_key_index ( id) ;
267
314
zalsa_local. report_tracked_read (
268
315
index,
269
- Durability :: MAX ,
316
+ durability ,
270
317
current_revision,
271
318
InputAccumulatedValues :: Empty ,
272
319
) ;
@@ -286,10 +333,13 @@ where
286
333
/// to the interned item.
287
334
pub fn data < ' db > ( & ' db self , db : & ' db dyn Database , id : Id ) -> & ' db C :: Fields < ' db > {
288
335
let internal_data = db. zalsa ( ) . table ( ) . get :: < Value < C > > ( id) ;
336
+ let last_changed_revision = db. zalsa ( ) . last_changed_revision ( internal_data. durability ( ) ) ;
337
+
289
338
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 ."
292
341
) ;
342
+
293
343
unsafe { Self :: from_internal_data ( & internal_data. fields ) }
294
344
}
295
345
0 commit comments