@@ -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,28 @@ 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 ! (
566
+ swapped_out. is_none( ) ,
567
+ "two concurrent writers to {id:?}, should not be possible"
568
+ ) ;
567
569
}
568
570
569
571
/// Fetch the data for a given id created by this ingredient from the table,
570
572
/// -giving it the appropriate type.
571
- fn data ( table : & Table , id : Id ) -> & Value < C > {
572
- table. get ( id)
573
+ fn data ( table : & Table , id : Id , current_revision : Revision ) -> & Value < C > {
574
+ let val = Self :: data_raw ( table, id) ;
575
+ acquire_read_lock ( unsafe { & ( * val) . updated_at } , current_revision) ;
576
+ // We have acquired the read lock, so it is safe to return a reference to the data.
577
+ unsafe { & * val }
573
578
}
574
579
575
580
fn data_raw ( table : & Table , id : Id ) -> * mut Value < C > {
@@ -594,29 +599,23 @@ where
594
599
} ) ;
595
600
596
601
let zalsa = db. zalsa ( ) ;
597
- let current_revision = zalsa. current_revision ( ) ;
598
602
let data = Self :: data_raw ( zalsa. table ( ) , id) ;
599
603
600
604
// We want to set `updated_at` to `None`, signalling that other field values
601
605
// 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 ( ) {
606
+ match unsafe { ( * data) . updated_at . swap ( None ) } {
604
607
None => {
605
608
panic ! ( "cannot delete write-locked id `{id:?}`; value leaked across threads" ) ;
606
609
}
607
- Some ( r) if r == current_revision => panic ! (
610
+ Some ( r) if r == zalsa . current_revision ( ) => panic ! (
608
611
"cannot delete read-locked id `{id:?}`; value leaked across threads or user functions not deterministic"
609
612
) ,
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
- }
613
+ Some ( _) => ( )
615
614
}
616
615
617
616
// 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 ( ) } ;
617
+ // signalling that we have acquired the write lock
618
+ let memo_table = std :: mem :: take ( unsafe { & mut ( * data) . memos } ) ;
620
619
621
620
// SAFETY: We have verified that no more references to these memos exist and so we are good
622
621
// to drop them.
@@ -648,7 +647,7 @@ where
648
647
s : C :: Struct < ' db > ,
649
648
) -> & ' db C :: Fields < ' db > {
650
649
let id = C :: deref_struct ( s) ;
651
- let value = Self :: data ( db. zalsa ( ) . table ( ) , id) ;
650
+ let value = Self :: data ( db. zalsa ( ) . table ( ) , id, db . zalsa ( ) . current_revision ( ) ) ;
652
651
unsafe { self . to_self_ref ( & value. fields ) }
653
652
}
654
653
@@ -670,9 +669,7 @@ where
670
669
let ( zalsa, zalsa_local) = db. zalsas ( ) ;
671
670
let id = C :: deref_struct ( s) ;
672
671
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 ( ) ) ;
672
+ let data = Self :: data ( zalsa. table ( ) , id, zalsa. current_revision ( ) ) ;
676
673
677
674
let field_changed_at = data. revisions [ relative_tracked_index] ;
678
675
@@ -697,9 +694,7 @@ where
697
694
) -> & ' db C :: Fields < ' db > {
698
695
let ( zalsa, zalsa_local) = db. zalsas ( ) ;
699
696
let id = C :: deref_struct ( s) ;
700
- let data = Self :: data ( zalsa. table ( ) , id) ;
701
-
702
- data. read_lock ( zalsa. current_revision ( ) ) ;
697
+ let data = Self :: data ( zalsa. table ( ) , id, zalsa. current_revision ( ) ) ;
703
698
704
699
// Add a dependency on the tracked struct itself.
705
700
zalsa_local. report_tracked_read (
@@ -742,7 +737,7 @@ where
742
737
revision : Revision ,
743
738
) -> MaybeChangedAfter {
744
739
let zalsa = db. zalsa ( ) ;
745
- let data = Self :: data ( zalsa. table ( ) , input) ;
740
+ let data = Self :: data ( zalsa. table ( ) , input, zalsa . current_revision ( ) ) ;
746
741
747
742
MaybeChangedAfter :: from ( data. created_at > revision)
748
743
}
@@ -761,9 +756,7 @@ where
761
756
_executor : DatabaseKeyIndex ,
762
757
_output_key : crate :: Id ,
763
758
) {
764
- // we used to update `update_at` field but now we do it lazilly when data is accessed
765
- //
766
- // FIXME: delete this method
759
+ // we used to update `update_at` field but now we do it lazily when data is accessed
767
760
}
768
761
769
762
fn remove_stale_output (
@@ -776,7 +769,7 @@ where
776
769
// `executor` creates a tracked struct `salsa_output_key`,
777
770
// but it did not in the current revision.
778
771
// 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) ;
772
+ self . delete_entity ( db, stale_output_key) ;
780
773
}
781
774
782
775
fn fmt_index ( & self , index : Option < crate :: Id > , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
@@ -811,34 +804,22 @@ where
811
804
pub fn fields ( & self ) -> & C :: Fields < ' static > {
812
805
& self . fields
813
806
}
807
+ }
814
808
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
- }
809
+ fn acquire_read_lock ( updated_at : & OptionalAtomicRevision , current_revision : Revision ) {
810
+ loop {
811
+ match updated_at. load ( ) {
812
+ None => panic ! (
813
+ "write lock taken; value leaked across threads or user functions not deterministic"
814
+ ) ,
815
+ // the read lock was taken by someone else, so we also succeed
816
+ Some ( r) if r == current_revision => return ,
817
+ Some ( r) => {
818
+ if updated_at
819
+ . compare_exchange ( Some ( r) , Some ( current_revision) )
820
+ . is_ok ( )
821
+ {
822
+ break ;
842
823
}
843
824
}
844
825
}
@@ -849,23 +830,25 @@ impl<C> Slot for Value<C>
849
830
where
850
831
C : Configuration ,
851
832
{
833
+ // FIXME: `&self` may alias here before the lock is taken?
852
834
unsafe fn memos ( & self , current_revision : Revision ) -> & crate :: table:: memo:: MemoTable {
853
835
// Acquiring the read lock here with the current revision
854
836
// ensures that there is no danger of a race
855
837
// when deleting a tracked struct.
856
- self . read_lock ( current_revision) ;
838
+ acquire_read_lock ( & self . updated_at , current_revision) ;
857
839
& self . memos
858
840
}
859
841
860
842
fn memos_mut ( & mut self ) -> & mut crate :: table:: memo:: MemoTable {
861
843
& mut self . memos
862
844
}
863
845
846
+ // FIXME: `&self` may alias here?
864
847
unsafe fn syncs ( & self , current_revision : Revision ) -> & crate :: table:: sync:: SyncTable {
865
848
// Acquiring the read lock here with the current revision
866
849
// ensures that there is no danger of a race
867
850
// when deleting a tracked struct.
868
- self . read_lock ( current_revision) ;
851
+ acquire_read_lock ( & self . updated_at , current_revision) ;
869
852
& self . syncs
870
853
}
871
854
}
0 commit comments