@@ -400,18 +400,17 @@ where
400400 disambiguator,
401401 } ;
402402
403- let current_revision = zalsa. current_revision ( ) ;
404403 match zalsa_local. tracked_struct_id ( & identity) {
405404 Some ( id) => {
406405 // The struct already exists in the intern map.
407406 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) ;
409408 C :: struct_from_id ( id)
410409 }
411410
412411 None => {
413412 // 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) ;
415414 let key = self . database_key_index ( id) ;
416415 zalsa_local. add_output ( key. into ( ) ) ;
417416 zalsa_local. store_tracked_struct_id ( identity, id) ;
@@ -424,10 +423,10 @@ where
424423 & ' db self ,
425424 zalsa : & ' db Zalsa ,
426425 zalsa_local : & ' db ZalsaLocal ,
427- current_revision : Revision ,
428426 current_deps : & StampedValue < ( ) > ,
429427 fields : C :: Fields < ' db > ,
430428 ) -> Id {
429+ let current_revision = zalsa. current_revision ( ) ;
431430 let value = |_| Value {
432431 created_at : current_revision,
433432 updated_at : OptionalAtomicRevision :: new ( Some ( current_revision) ) ,
@@ -440,16 +439,14 @@ where
440439
441440 if let Some ( id) = self . free_list . pop ( ) {
442441 let data_raw = Self :: data_raw ( zalsa. table ( ) , id) ;
443- assert ! (
442+ debug_assert ! (
444443 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 "
446445 ) ;
447446
448447 // Overwrite the free-list entry. Use `*foo = ` because the entry
449448 // 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) } ;
453450
454451 id
455452 } else {
@@ -467,7 +464,6 @@ where
467464 fn update < ' db > (
468465 & ' db self ,
469466 zalsa : & ' db Zalsa ,
470- current_revision : Revision ,
471467 id : Id ,
472468 current_deps : & StampedValue < ( ) > ,
473469 fields : C :: Fields < ' db > ,
@@ -508,6 +504,7 @@ where
508504 // during the current revision and thus obtained an `&` reference to those fields
509505 // that is still live.
510506
507+ let current_revision = zalsa. current_revision ( ) ;
511508 // UNSAFE: Marking as mut requires exclusive access for the duration of
512509 // the `mut`. We have now *claimed* this data by swapping in `None`,
513510 // any attempt to read concurrently will panic.
@@ -524,17 +521,19 @@ where
524521 // Acquire the write-lock. This can only fail if there is a parallel thread
525522 // reading from this same `id`, which can only happen if the user has leaked it.
526523 // 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 {
529527 panic ! (
530528 "failed to acquire write lock, id `{id:?}` must have been leaked across threads"
531529 ) ;
532530 }
533531
534- // UNSAFE : Marking as mut requires exclusive access for the duration of
532+ // SAFETY : Marking as mut requires exclusive access for the duration of
535533 // 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.
538537
539538 // SAFETY: We assert that the pointer to `data.revisions`
540539 // is a pointer into the database referencing a value
@@ -544,8 +543,8 @@ where
544543 unsafe {
545544 if C :: update_fields (
546545 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) ) ,
549548 fields,
550549 ) {
551550 // Consider this a new tracked-struct (even though it still uses the same id)
@@ -554,22 +553,28 @@ where
554553 // which makes Salsa consider two tracked structs to still be the same
555554 // even though the fields are different.
556555 // 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;
558561 }
562+ ( * data_raw) . durability = current_deps. durability ;
559563 }
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+ ) ;
567569 }
568570
569571 /// Fetch the data for a given id created by this ingredient from the table,
570572 /// -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 }
573578 }
574579
575580 fn data_raw ( table : & Table , id : Id ) -> * mut Value < C > {
@@ -594,29 +599,23 @@ where
594599 } ) ;
595600
596601 let zalsa = db. zalsa ( ) ;
597- let current_revision = zalsa. current_revision ( ) ;
598602 let data = Self :: data_raw ( zalsa. table ( ) , id) ;
599603
600604 // We want to set `updated_at` to `None`, signalling that other field values
601605 // 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 ) } {
604607 None => {
605608 panic ! ( "cannot delete write-locked id `{id:?}`; value leaked across threads" ) ;
606609 }
607- Some ( r) if r == current_revision => panic ! (
610+ Some ( r) if r == zalsa . current_revision ( ) => panic ! (
608611 "cannot delete read-locked id `{id:?}`; value leaked across threads or user functions not deterministic"
609612 ) ,
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 ( _) => ( )
615614 }
616615
617616 // 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 } ) ;
620619
621620 // SAFETY: We have verified that no more references to these memos exist and so we are good
622621 // to drop them.
@@ -648,7 +647,7 @@ where
648647 s : C :: Struct < ' db > ,
649648 ) -> & ' db C :: Fields < ' db > {
650649 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 ( ) ) ;
652651 unsafe { self . to_self_ref ( & value. fields ) }
653652 }
654653
@@ -670,9 +669,7 @@ where
670669 let ( zalsa, zalsa_local) = db. zalsas ( ) ;
671670 let id = C :: deref_struct ( s) ;
672671 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 ( ) ) ;
676673
677674 let field_changed_at = data. revisions [ relative_tracked_index] ;
678675
@@ -697,9 +694,7 @@ where
697694 ) -> & ' db C :: Fields < ' db > {
698695 let ( zalsa, zalsa_local) = db. zalsas ( ) ;
699696 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 ( ) ) ;
703698
704699 // Add a dependency on the tracked struct itself.
705700 zalsa_local. report_tracked_read (
@@ -742,7 +737,7 @@ where
742737 revision : Revision ,
743738 ) -> MaybeChangedAfter {
744739 let zalsa = db. zalsa ( ) ;
745- let data = Self :: data ( zalsa. table ( ) , input) ;
740+ let data = Self :: data ( zalsa. table ( ) , input, zalsa . current_revision ( ) ) ;
746741
747742 MaybeChangedAfter :: from ( data. created_at > revision)
748743 }
@@ -761,9 +756,7 @@ where
761756 _executor : DatabaseKeyIndex ,
762757 _output_key : crate :: Id ,
763758 ) {
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
767760 }
768761
769762 fn remove_stale_output (
@@ -776,7 +769,7 @@ where
776769 // `executor` creates a tracked struct `salsa_output_key`,
777770 // but it did not in the current revision.
778771 // 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) ;
780773 }
781774
782775 fn fmt_index ( & self , index : Option < crate :: Id > , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
@@ -811,34 +804,22 @@ where
811804 pub fn fields ( & self ) -> & C :: Fields < ' static > {
812805 & self . fields
813806 }
807+ }
814808
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 ;
842823 }
843824 }
844825 }
@@ -849,23 +830,25 @@ impl<C> Slot for Value<C>
849830where
850831 C : Configuration ,
851832{
833+ // FIXME: `&self` may alias here before the lock is taken?
852834 unsafe fn memos ( & self , current_revision : Revision ) -> & crate :: table:: memo:: MemoTable {
853835 // Acquiring the read lock here with the current revision
854836 // ensures that there is no danger of a race
855837 // when deleting a tracked struct.
856- self . read_lock ( current_revision) ;
838+ acquire_read_lock ( & self . updated_at , current_revision) ;
857839 & self . memos
858840 }
859841
860842 fn memos_mut ( & mut self ) -> & mut crate :: table:: memo:: MemoTable {
861843 & mut self . memos
862844 }
863845
846+ // FIXME: `&self` may alias here?
864847 unsafe fn syncs ( & self , current_revision : Revision ) -> & crate :: table:: sync:: SyncTable {
865848 // Acquiring the read lock here with the current revision
866849 // ensures that there is no danger of a race
867850 // when deleting a tracked struct.
868- self . read_lock ( current_revision) ;
851+ acquire_read_lock ( & self . updated_at , current_revision) ;
869852 & self . syncs
870853 }
871854}
0 commit comments