@@ -271,15 +271,6 @@ where
271
271
/// with different values.
272
272
durability : Durability ,
273
273
274
- /// The revision in which the tracked struct was first created.
275
- ///
276
- /// Unlike `updated_at`, which gets bumped on every read,
277
- /// `created_at` is updated whenever an untracked field is updated.
278
- /// This is necessary to detect reused tracked struct ids _after_
279
- /// they've been freed in a prior revision or tracked structs that have been updated
280
- /// in-place because of a bad `Hash` implementation.
281
- created_at : Revision ,
282
-
283
274
/// The revision when this tracked struct was last updated.
284
275
/// This field also acts as a kind of "lock". Once it is equal
285
276
/// to `Some(current_revision)`, the fields are locked and
@@ -400,7 +391,7 @@ where
400
391
tracing:: trace!( "Reuse tracked struct {id:?}" , id = index) ;
401
392
// The struct already exists in the intern map.
402
393
zalsa_local. add_output ( index) ;
403
- self . update ( zalsa, current_revision, id, & current_deps, fields) ;
394
+ let id = self . update ( zalsa, current_revision, id, & current_deps, fields) ;
404
395
FromId :: from_id ( id)
405
396
}
406
397
@@ -425,7 +416,6 @@ where
425
416
fields : C :: Fields < ' db > ,
426
417
) -> Id {
427
418
let value = |_| Value {
428
- created_at : current_revision,
429
419
updated_at : OptionalAtomicRevision :: new ( Some ( current_revision) ) ,
430
420
durability : current_deps. durability ,
431
421
// lifetime erase for storage
@@ -434,17 +424,11 @@ where
434
424
memos : Default :: default ( ) ,
435
425
} ;
436
426
437
- while let Some ( id) = self . free_list . pop ( ) {
427
+ if let Some ( id) = self . free_list . pop ( ) {
438
428
// Increment the ID generation before reusing it, as if we have allocated a new
439
429
// slot in the table.
440
- //
441
- // If the generation will wrap, we are forced to leak the slot. We reserve enough
442
- // bits for the generation that this should not be a problem in practice.
443
- let Some ( generation) = id. generation ( ) . checked_add ( 1 ) else {
444
- continue ;
445
- } ;
446
-
447
- let id = id. with_generation ( generation) ;
430
+ let id = id. with_generation ( id. generation ( ) + 1 ) ;
431
+
448
432
return Self :: data_raw ( zalsa. table ( ) , id) . with_mut ( |data_raw| {
449
433
let data_raw = data_raw. cast :: < Value < C > > ( ) ;
450
434
@@ -477,10 +461,10 @@ where
477
461
& ' db self ,
478
462
zalsa : & ' db Zalsa ,
479
463
current_revision : Revision ,
480
- id : Id ,
464
+ mut id : Id ,
481
465
current_deps : & StampedValue < ( ) > ,
482
466
fields : C :: Fields < ' db > ,
483
- ) {
467
+ ) -> Id {
484
468
let data_raw = Self :: data_raw ( zalsa. table ( ) , id) ;
485
469
486
470
// The protocol is:
@@ -547,7 +531,7 @@ where
547
531
} ) ;
548
532
549
533
if !locked {
550
- return ;
534
+ return id ;
551
535
}
552
536
553
537
data_raw. with_mut ( |data| {
@@ -570,22 +554,25 @@ where
570
554
) ,
571
555
fields,
572
556
) {
573
- // Consider this a new tracked-struct (even though it still uses the same id)
574
- // when any non-tracked field got updated.
575
- // This should be rare and only ever happen if there's a hash collision
576
- // which makes Salsa consider two tracked structs to still be the same
577
- // even though the fields are different.
578
- // See `tracked-struct-id-field-bad-hash` for more details.
579
- data. created_at = current_revision;
557
+ // Consider this a new tracked-struct when any non-tracked field got updated.
558
+ // This should be rare and only ever happen if there's a hash collision.
559
+ //
560
+ // Note that we hold the lock and have exclusive access to the tracked struct data,
561
+ // so there should be no live instances of IDs from the previous generation. We clear
562
+ // the memos and return a new ID here as if we have allocated a new slot.
563
+ let mut table = data. take_memo_table ( ) ;
564
+ self . clear_memos ( zalsa, & mut table, id) ;
565
+ id = id. with_generation ( id. generation ( ) + 1 ) ;
580
566
}
581
567
}
582
568
if current_deps. durability < data. durability {
583
569
data. revisions = C :: new_revisions ( current_deps. changed_at ) ;
584
- data. created_at = current_revision;
585
570
}
586
571
data. durability = current_deps. durability ;
587
572
let swapped_out = data. updated_at . swap ( Some ( current_revision) ) ;
588
573
assert ! ( swapped_out. is_none( ) ) ;
574
+
575
+ id
589
576
} )
590
577
}
591
578
@@ -645,21 +632,26 @@ where
645
632
unsafe { ( * data) . assume_init_mut ( ) } . take_memo_table ( )
646
633
} ) ;
647
634
635
+ self . clear_memos ( zalsa, & mut memo_table, id) ;
636
+ }
637
+
638
+ /// Clears the given memo table.
639
+ pub ( crate ) fn clear_memos ( & self , zalsa : & Zalsa , memo_table : & mut MemoTable , id : Id ) {
648
640
// SAFETY: We use the correct types table.
649
- let table = unsafe { self . memo_table_types . attach_memos_mut ( & mut memo_table) } ;
641
+ let table = unsafe { self . memo_table_types . attach_memos_mut ( memo_table) } ;
650
642
651
643
// `Database::salsa_event` is a user supplied callback which may panic
652
644
// in that case we need a drop guard to free the memo table
653
645
struct TableDropGuard < ' a > ( MemoTableWithTypesMut < ' a > ) ;
654
646
impl Drop for TableDropGuard < ' _ > {
655
647
fn drop ( & mut self ) {
656
- // SAFETY: We have verified that no more references to these memos exist and so we are good
648
+ // SAFETY: We have `&mut MemoTable`, so no more references to these memos exist and we are good
657
649
// to drop them.
658
650
unsafe { self . 0 . drop ( ) } ;
659
651
}
660
652
}
661
653
let mut table_guard = TableDropGuard ( table) ;
662
- // SAFETY: We have verified that no more references to these memos exist and so we are good
654
+ // SAFETY: We have `&mut MemoTable`, so no more references to these memos exist and we are good
663
655
// to drop them.
664
656
unsafe {
665
657
table_guard. 0 . take_memos ( |memo_ingredient_index, memo| {
@@ -767,15 +759,13 @@ where
767
759
768
760
unsafe fn maybe_changed_after (
769
761
& self ,
770
- db : & dyn Database ,
771
- input : Id ,
772
- revision : Revision ,
762
+ _db : & dyn Database ,
763
+ _input : Id ,
764
+ _revision : Revision ,
773
765
_cycle_heads : & mut CycleHeads ,
774
766
) -> VerifyResult {
775
- let zalsa = db. zalsa ( ) ;
776
- let data = Self :: data ( zalsa. table ( ) , input) ;
777
-
778
- VerifyResult :: changed_if ( data. created_at > revision)
767
+ // Any change to a tracked struct results in a new ID generation.
768
+ VerifyResult :: unchanged ( )
779
769
}
780
770
781
771
fn mark_validated_output (
@@ -799,7 +789,7 @@ where
799
789
// `executor` creates a tracked struct `salsa_output_key`,
800
790
// but it did not in the current revision.
801
791
// In that case, we can delete `stale_output_key` and any data associated with it.
802
- self . delete_entity ( zalsa, stale_output_key) ;
792
+ self . delete_entity ( zalsa, stale_output_key)
803
793
}
804
794
805
795
fn debug_name ( & self ) -> & ' static str {
0 commit comments