@@ -103,13 +103,14 @@ where
103
103
struct ValueShared {
104
104
/// The interned ID for this value.
105
105
///
106
- /// This is necessary to identify slots in the LRU list.
106
+ /// Storing this on the value itself is necessary to identify slots
107
+ /// from the LRU list.
107
108
id : Id ,
108
109
109
110
/// The revision the value was first interned in.
110
111
first_interned_at : Revision ,
111
112
112
- /// The most recent interned revision .
113
+ /// The revision the value was most-recently interned in .
113
114
last_interned_at : Revision ,
114
115
115
116
/// The minimum durability of all inputs consumed by the creator
@@ -327,8 +328,16 @@ where
327
328
if let Some ( value) = cursor. get ( ) {
328
329
let is_stale = value. shared . with ( |value_shared| {
329
330
// SAFETY: We hold the lock.
330
- let last_interned_at = unsafe { ( * value_shared) . last_interned_at } ;
331
- self . revision_queue . is_stale ( last_interned_at)
331
+ let value_shared = unsafe { & * value_shared } ;
332
+
333
+ // The value must not have been read in the current revision to be collected
334
+ // safely, but we also do not want to collect values that have been read recently.
335
+ let is_stale = self . revision_queue . is_stale ( value_shared. last_interned_at ) ;
336
+
337
+ // We can't collect higher durability values until we have a mutable reference to the database.
338
+ let is_low_durability = value_shared. durability == Durability :: LOW ;
339
+
340
+ is_stale && is_low_durability
332
341
} ) ;
333
342
334
343
if is_stale {
@@ -337,34 +346,20 @@ where
337
346
. active_query ( )
338
347
. map ( |( _, stamp) | ( stamp. durability , current_revision) )
339
348
// If there is no active query this durability does not actually matter.
340
- // `last_interned_at` needs to be `Revision::MAX`, see the intern_access_in_different_revision test.
349
+ // `last_interned_at` needs to be `Revision::MAX`, see the ` intern_access_in_different_revision` test.
341
350
. unwrap_or ( ( Durability :: MAX , Revision :: max ( ) ) ) ;
342
351
343
- let value = value. shared . get_mut ( ) . with ( |value_shared| {
352
+ let id = value. shared . get_mut ( ) . with ( |value_shared| {
344
353
// SAFETY: We hold the lock.
345
354
let value_shared = unsafe { & mut * value_shared } ;
346
355
347
356
// Mark the slot as reused.
348
357
value_shared. first_interned_at = current_revision;
349
358
value_shared. last_interned_at = last_interned_at;
350
-
351
- // Remove the value from the LRU list.
352
- //
353
- // SAFETY: The value pointer is valid for the lifetime of the database.
354
- unsafe { & * UnsafeRef :: into_raw ( cursor. remove ( ) . unwrap ( ) ) }
355
- } ) ;
356
-
357
- let id = value. shared . with_mut ( |value_shared| {
358
- // SAFETY: We hold the lock.
359
- let value_shared = unsafe { & mut * value_shared } ;
360
-
361
- // Note we need to retain the previous durability here to ensure queries trying
362
- // to read the old value are revalidated.
363
- value_shared. durability = std:: cmp:: max ( value_shared. durability , durability) ;
364
-
365
- let index = self . database_key_index ( value_shared. id ) ;
359
+ value_shared. durability = durability;
366
360
367
361
// Record a dependency on the value.
362
+ let index = self . database_key_index ( value_shared. id ) ;
368
363
zalsa_local. report_tracked_read_simple (
369
364
index,
370
365
value_shared. durability ,
@@ -381,12 +376,59 @@ where
381
376
value_shared. id
382
377
} ) ;
383
378
379
+ // Remove the value from the LRU list.
380
+ //
381
+ // SAFETY: The value pointer is valid for the lifetime of the database.
382
+ let value = unsafe { & * UnsafeRef :: into_raw ( cursor. remove ( ) . unwrap ( ) ) } ;
383
+
384
384
// Reuse the value slot with the new data.
385
385
//
386
386
// SAFETY: We hold the lock and marked the value as reused, so any readers in the
387
387
// current revision will see it is not valid.
388
- value. fields . with_mut ( |fields| unsafe {
389
- * fields = self . to_internal_data ( assemble ( id, key) ) ;
388
+ value. fields . with_mut ( |old_fields| {
389
+ let old_fields = unsafe { & mut * old_fields } ;
390
+
391
+ // Remove the old value from the ID map.
392
+ let old_fields_ref = & * old_fields;
393
+
394
+ let eq = |id : & _ | {
395
+ let data = table. get :: < Value < C > > ( * id) ;
396
+
397
+ data. fields . with ( |fields| {
398
+ // SAFETY: We hold the lock.
399
+ let fields = unsafe { & * fields } ;
400
+
401
+ // SAFETY: it's safe to go from Data<'static> to Data<'db>
402
+ // shrink lifetime here to use a single lifetime in Lookup::eq(&StructKey<'db>, &C::Data<'db>)
403
+ let data = unsafe { Self :: from_internal_data ( fields) } ;
404
+
405
+ data == old_fields_ref
406
+ } )
407
+ } ;
408
+
409
+ let old_data_hash = self . hasher . hash_one ( old_fields_ref) ;
410
+ shared
411
+ . key_map
412
+ . find_entry ( old_data_hash, eq)
413
+ . expect ( "interned value in LRU so must be in key_map" )
414
+ . remove ( ) ;
415
+
416
+ // Update the fields.
417
+ * old_fields = unsafe { self . to_internal_data ( assemble ( id, key) ) } ;
418
+
419
+ // Insert the new value into the ID map.
420
+ let hasher = |id : & _ | {
421
+ // This closure is only called if the table is resized. So while it's expensive
422
+ // to lookup all values, it will only happen rarely.
423
+ let value = zalsa. table ( ) . get :: < Value < C > > ( * id) ;
424
+
425
+ // SAFETY: We hold the lock.
426
+ value
427
+ . fields
428
+ . with ( |fields| unsafe { self . hasher . hash_one ( & * fields) } )
429
+ } ;
430
+
431
+ shared. key_map . insert_unique ( data_hash, id, hasher) ;
390
432
} ) ;
391
433
392
434
// TODO: Need to free the memory safely here.
@@ -436,7 +478,7 @@ where
436
478
. active_query ( )
437
479
. map ( |( _, stamp) | ( stamp. durability , current_revision) )
438
480
// If there is no active query this durability does not actually matter.
439
- // `last_interned_at` needs to be `Revision::MAX`, see the intern_access_in_different_revision test.
481
+ // `last_interned_at` needs to be `Revision::MAX`, see the ` intern_access_in_different_revision` test.
440
482
. unwrap_or ( ( Durability :: MAX , Revision :: max ( ) ) ) ;
441
483
442
484
// Allocate the value slot.
@@ -486,12 +528,8 @@ where
486
528
487
529
let index = self . database_key_index ( id) ;
488
530
489
- // SAFETY: We hold the lock.
490
- value. shared . with_mut ( |value_shared| {
491
- // Record a dependency on this value.
492
- let first_interned_at = unsafe { ( * value_shared) . first_interned_at } ;
493
- zalsa_local. report_tracked_read_simple ( index, durability, first_interned_at) ;
494
- } ) ;
531
+ // Record a dependency on the value.
532
+ zalsa_local. report_tracked_read_simple ( index, durability, current_revision) ;
495
533
496
534
zalsa. event ( & || {
497
535
Event :: new ( EventKind :: DidInternValue {
@@ -522,6 +560,10 @@ where
522
560
// SAFETY: We hold the lock.
523
561
let value_shared = unsafe { & * value_shared } ;
524
562
563
+ // Record a read dependency on the value.
564
+ //
565
+ // This is necessary as interned slots may be reused, in which case any queries
566
+ // that created or read from the previous value need to be revalidated.
525
567
zalsa_local. report_tracked_read_simple (
526
568
self . database_key_index ( id) ,
527
569
value_shared. durability ,
0 commit comments