@@ -400,18 +400,17 @@ where
400
400
disambiguator,
401
401
} ;
402
402
403
- let current_revision = zalsa. current_revision ( ) ;
404
403
match zalsa_local. tracked_struct_id ( & identity) {
405
404
Some ( id) => {
406
405
// The struct already exists in the intern map.
407
406
zalsa_local. add_output ( self . database_key_index ( id) . into ( ) ) ;
408
- self . update ( zalsa, current_revision , id, & current_deps, fields) ;
407
+ self . update ( zalsa, id, & current_deps, fields) ;
409
408
C :: struct_from_id ( id)
410
409
}
411
410
412
411
None => {
413
412
// This is a new tracked struct, so create an entry in the struct map.
414
- let id = self . allocate ( zalsa, zalsa_local, current_revision , & current_deps, fields) ;
413
+ let id = self . allocate ( zalsa, zalsa_local, & current_deps, fields) ;
415
414
let key = self . database_key_index ( id) ;
416
415
zalsa_local. add_output ( key. into ( ) ) ;
417
416
zalsa_local. store_tracked_struct_id ( identity, id) ;
@@ -424,10 +423,10 @@ where
424
423
& ' db self ,
425
424
zalsa : & ' db Zalsa ,
426
425
zalsa_local : & ' db ZalsaLocal ,
427
- current_revision : Revision ,
428
426
current_deps : & StampedValue < ( ) > ,
429
427
fields : C :: Fields < ' db > ,
430
428
) -> Id {
429
+ let current_revision = zalsa. current_revision ( ) ;
431
430
let value = |_| Value {
432
431
created_at : current_revision,
433
432
updated_at : OptionalAtomicRevision :: new ( Some ( current_revision) ) ,
@@ -440,16 +439,14 @@ where
440
439
441
440
if let Some ( id) = self . free_list . pop ( ) {
442
441
let data_raw = Self :: data_raw ( zalsa. table ( ) , id) ;
443
- assert ! (
442
+ debug_assert ! (
444
443
unsafe { ( * data_raw) . updated_at. load( ) . is_none( ) } ,
445
- "free list entry for `{id:?}` does not have `None` for `updated_at` "
444
+ "free list entry for `{id:?}` should not be locked "
446
445
) ;
447
446
448
447
// Overwrite the free-list entry. Use `*foo = ` because the entry
449
448
// has been previously initialized and we want to free the old contents.
450
- unsafe {
451
- * data_raw = value ( id) ;
452
- }
449
+ unsafe { * data_raw = value ( id) } ;
453
450
454
451
id
455
452
} else {
@@ -467,7 +464,6 @@ where
467
464
fn update < ' db > (
468
465
& ' db self ,
469
466
zalsa : & ' db Zalsa ,
470
- current_revision : Revision ,
471
467
id : Id ,
472
468
current_deps : & StampedValue < ( ) > ,
473
469
fields : C :: Fields < ' db > ,
@@ -508,6 +504,7 @@ where
508
504
// during the current revision and thus obtained an `&` reference to those fields
509
505
// that is still live.
510
506
507
+ let current_revision = zalsa. current_revision ( ) ;
511
508
// UNSAFE: Marking as mut requires exclusive access for the duration of
512
509
// the `mut`. We have now *claimed* this data by swapping in `None`,
513
510
// any attempt to read concurrently will panic.
@@ -524,17 +521,19 @@ where
524
521
// Acquire the write-lock. This can only fail if there is a parallel thread
525
522
// reading from this same `id`, which can only happen if the user has leaked it.
526
523
// Tsk tsk.
527
- let swapped_out = unsafe { ( * data_raw) . updated_at . swap ( None ) } ;
528
- if swapped_out != last_updated_at {
524
+
525
+ let swapped = unsafe { ( * data_raw) . updated_at . swap ( None ) } ;
526
+ if last_updated_at != swapped {
529
527
panic ! (
530
528
"failed to acquire write lock, id `{id:?}` must have been leaked across threads"
531
529
) ;
532
530
}
533
531
534
- // UNSAFE : Marking as mut requires exclusive access for the duration of
532
+ // SAFETY : Marking as mut requires exclusive access for the duration of
535
533
// the `mut`. We have now *claimed* this data by swapping in `None`,
536
- // any attempt to read concurrently will panic.
537
- let data = unsafe { & mut * data_raw } ;
534
+ // any attempt to read concurrently will panic. Note that we cannot create
535
+ // a `&mut` reference to the full `Value` though because
536
+ // another thread may access `updated_at` concurrently.
538
537
539
538
// SAFETY: We assert that the pointer to `data.revisions`
540
539
// is a pointer into the database referencing a value
@@ -544,8 +543,8 @@ where
544
543
unsafe {
545
544
if C :: update_fields (
546
545
current_revision,
547
- & mut data . revisions ,
548
- self . to_self_ptr ( std:: ptr:: addr_of_mut!( data . fields) ) ,
546
+ & mut ( * data_raw ) . revisions ,
547
+ self . to_self_ptr ( std:: ptr:: addr_of_mut!( ( * data_raw ) . fields) ) ,
549
548
fields,
550
549
) {
551
550
// Consider this a new tracked-struct (even though it still uses the same id)
@@ -554,22 +553,25 @@ where
554
553
// which makes Salsa consider two tracked structs to still be the same
555
554
// even though the fields are different.
556
555
// See `tracked-struct-id-field-bad-hash` for more details.
557
- data. created_at = current_revision;
556
+ ( * data_raw) . revisions = C :: new_revisions ( current_revision) ;
557
+ ( * data_raw) . created_at = current_revision;
558
+ } else if current_deps. durability < ( * data_raw) . durability {
559
+ ( * data_raw) . revisions = C :: new_revisions ( current_revision) ;
560
+ ( * data_raw) . created_at = current_revision;
558
561
}
562
+ ( * data_raw) . durability = current_deps. durability ;
559
563
}
560
- if current_deps. durability < data. durability {
561
- data. revisions = C :: new_revisions ( current_revision) ;
562
- data. created_at = current_revision;
563
- }
564
- data. durability = current_deps. durability ;
565
- let swapped_out = data. updated_at . swap ( Some ( current_revision) ) ;
566
- assert ! ( swapped_out. is_none( ) ) ;
564
+ let swapped_out = unsafe { ( * data_raw) . updated_at . swap_mut ( Some ( current_revision) ) } ;
565
+ assert ! ( swapped_out. is_none( ) , "lock was acquired twice!" ) ;
567
566
}
568
567
569
568
/// Fetch the data for a given id created by this ingredient from the table,
570
569
/// -giving it the appropriate type.
571
- fn data ( table : & Table , id : Id ) -> & Value < C > {
572
- table. get ( id)
570
+ fn data ( table : & Table , id : Id , current_revision : Revision ) -> & Value < C > {
571
+ let val = Self :: data_raw ( table, id) ;
572
+ acquire_read_lock ( unsafe { & ( * val) . updated_at } , current_revision) ;
573
+ // We have acquired the read lock, so it is safe to return a reference to the data.
574
+ unsafe { & * val }
573
575
}
574
576
575
577
fn data_raw ( table : & Table , id : Id ) -> * mut Value < C > {
@@ -594,29 +596,23 @@ where
594
596
} ) ;
595
597
596
598
let zalsa = db. zalsa ( ) ;
597
- let current_revision = zalsa. current_revision ( ) ;
598
599
let data = Self :: data_raw ( zalsa. table ( ) , id) ;
599
600
600
601
// We want to set `updated_at` to `None`, signalling that other field values
601
602
// cannot be read. The current value should be `Some(R0)` for some older revision.
602
- let data_ref = unsafe { & * data } ;
603
- match data_ref. updated_at . load ( ) {
603
+ match unsafe { ( * data) . updated_at . swap ( None ) } {
604
604
None => {
605
605
panic ! ( "cannot delete write-locked id `{id:?}`; value leaked across threads" ) ;
606
606
}
607
- Some ( r) if r == current_revision => panic ! (
607
+ Some ( r) if r == zalsa . current_revision ( ) => panic ! (
608
608
"cannot delete read-locked id `{id:?}`; value leaked across threads or user functions not deterministic"
609
609
) ,
610
- Some ( r) => {
611
- if data_ref. updated_at . compare_exchange ( Some ( r) , None ) . is_err ( ) {
612
- panic ! ( "race occurred when deleting value `{id:?}`" )
613
- }
614
- }
610
+ Some ( _) => ( )
615
611
}
616
612
617
613
// Take the memo table. This is safe because we have modified `data_ref.updated_at` to `None`
618
- // and the code that references the memo-table has a read- lock.
619
- let memo_table = unsafe { ( * data) . take_memo_table ( ) } ;
614
+ // signalling that we have acquired the write lock
615
+ let memo_table = std :: mem :: take ( unsafe { & mut ( * data) . memos } ) ;
620
616
621
617
// SAFETY: We have verified that no more references to these memos exist and so we are good
622
618
// to drop them.
@@ -648,7 +644,7 @@ where
648
644
s : C :: Struct < ' db > ,
649
645
) -> & ' db C :: Fields < ' db > {
650
646
let id = C :: deref_struct ( s) ;
651
- let value = Self :: data ( db. zalsa ( ) . table ( ) , id) ;
647
+ let value = Self :: data ( db. zalsa ( ) . table ( ) , id, db . zalsa ( ) . current_revision ( ) ) ;
652
648
unsafe { self . to_self_ref ( & value. fields ) }
653
649
}
654
650
@@ -670,9 +666,7 @@ where
670
666
let ( zalsa, zalsa_local) = db. zalsas ( ) ;
671
667
let id = C :: deref_struct ( s) ;
672
668
let field_ingredient_index = self . ingredient_index . successor ( relative_tracked_index) ;
673
- let data = Self :: data ( zalsa. table ( ) , id) ;
674
-
675
- data. read_lock ( zalsa. current_revision ( ) ) ;
669
+ let data = Self :: data ( zalsa. table ( ) , id, zalsa. current_revision ( ) ) ;
676
670
677
671
let field_changed_at = data. revisions [ relative_tracked_index] ;
678
672
@@ -697,9 +691,7 @@ where
697
691
) -> & ' db C :: Fields < ' db > {
698
692
let ( zalsa, zalsa_local) = db. zalsas ( ) ;
699
693
let id = C :: deref_struct ( s) ;
700
- let data = Self :: data ( zalsa. table ( ) , id) ;
701
-
702
- data. read_lock ( zalsa. current_revision ( ) ) ;
694
+ let data = Self :: data ( zalsa. table ( ) , id, zalsa. current_revision ( ) ) ;
703
695
704
696
// Add a dependency on the tracked struct itself.
705
697
zalsa_local. report_tracked_read (
@@ -742,7 +734,7 @@ where
742
734
revision : Revision ,
743
735
) -> MaybeChangedAfter {
744
736
let zalsa = db. zalsa ( ) ;
745
- let data = Self :: data ( zalsa. table ( ) , input) ;
737
+ let data = Self :: data ( zalsa. table ( ) , input, zalsa . current_revision ( ) ) ;
746
738
747
739
MaybeChangedAfter :: from ( data. created_at > revision)
748
740
}
@@ -761,9 +753,7 @@ where
761
753
_executor : DatabaseKeyIndex ,
762
754
_output_key : crate :: Id ,
763
755
) {
764
- // we used to update `update_at` field but now we do it lazilly when data is accessed
765
- //
766
- // FIXME: delete this method
756
+ // we used to update `update_at` field but now we do it lazily when data is accessed
767
757
}
768
758
769
759
fn remove_stale_output (
@@ -776,7 +766,7 @@ where
776
766
// `executor` creates a tracked struct `salsa_output_key`,
777
767
// but it did not in the current revision.
778
768
// In that case, we can delete `stale_output_key` and any data associated with it.
779
- self . delete_entity ( db. as_dyn_database ( ) , stale_output_key) ;
769
+ self . delete_entity ( db, stale_output_key) ;
780
770
}
781
771
782
772
fn fmt_index ( & self , index : Option < crate :: Id > , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
@@ -811,34 +801,22 @@ where
811
801
pub fn fields ( & self ) -> & C :: Fields < ' static > {
812
802
& self . fields
813
803
}
804
+ }
814
805
815
- fn take_memo_table ( & mut self ) -> MemoTable {
816
- // This fn is only called after `updated_at` has been set to `None`;
817
- // this ensures that there is no concurrent access
818
- // (and that the `&mut self` is accurate...).
819
- assert ! ( self . updated_at. load( ) . is_none( ) ) ;
820
-
821
- std:: mem:: take ( & mut self . memos )
822
- }
823
-
824
- fn read_lock ( & self , current_revision : Revision ) {
825
- loop {
826
- match self . updated_at . load ( ) {
827
- None => {
828
- panic ! ( "access to field whilst the value is being initialized" ) ;
829
- }
830
- Some ( r) => {
831
- if r == current_revision {
832
- return ;
833
- }
834
-
835
- if self
836
- . updated_at
837
- . compare_exchange ( Some ( r) , Some ( current_revision) )
838
- . is_ok ( )
839
- {
840
- break ;
841
- }
806
+ fn acquire_read_lock ( updated_at : & OptionalAtomicRevision , current_revision : Revision ) {
807
+ loop {
808
+ match updated_at. load ( ) {
809
+ None => panic ! (
810
+ "write lock taken; value leaked across threads or user functions not deterministic"
811
+ ) ,
812
+ // the read lock was taken by someone else, so we also succeed
813
+ Some ( r) if r == current_revision => return ,
814
+ Some ( r) => {
815
+ if updated_at
816
+ . compare_exchange ( Some ( r) , Some ( current_revision) )
817
+ . is_ok ( )
818
+ {
819
+ break ;
842
820
}
843
821
}
844
822
}
@@ -849,23 +827,25 @@ impl<C> Slot for Value<C>
849
827
where
850
828
C : Configuration ,
851
829
{
830
+ // FIXME: `&self` may alias here before the lock is taken?
852
831
unsafe fn memos ( & self , current_revision : Revision ) -> & crate :: table:: memo:: MemoTable {
853
832
// Acquiring the read lock here with the current revision
854
833
// ensures that there is no danger of a race
855
834
// when deleting a tracked struct.
856
- self . read_lock ( current_revision) ;
835
+ acquire_read_lock ( & self . updated_at , current_revision) ;
857
836
& self . memos
858
837
}
859
838
860
839
fn memos_mut ( & mut self ) -> & mut crate :: table:: memo:: MemoTable {
861
840
& mut self . memos
862
841
}
863
842
843
+ // FIXME: `&self` may alias here?
864
844
unsafe fn syncs ( & self , current_revision : Revision ) -> & crate :: table:: sync:: SyncTable {
865
845
// Acquiring the read lock here with the current revision
866
846
// ensures that there is no danger of a race
867
847
// when deleting a tracked struct.
868
- self . read_lock ( current_revision) ;
848
+ acquire_read_lock ( & self . updated_at , current_revision) ;
869
849
& self . syncs
870
850
}
871
851
}
0 commit comments