@@ -20,7 +20,7 @@ use std::{
20
20
use rustc_index:: vec:: { Idx , IndexVec } ;
21
21
use rustc_target:: abi:: Size ;
22
22
use rustc_middle:: ty:: layout:: TyAndLayout ;
23
- use rustc_data_structures:: fx:: FxHashSet ;
23
+ use rustc_data_structures:: fx:: { FxHashSet , FxHashMap } ;
24
24
25
25
use crate :: {
26
26
MiriEvalContext , MiriEvalContextExt ,
@@ -662,7 +662,7 @@ impl VClockAlloc {
662
662
let ( index, clocks) = self . global . current_thread_state ( ) ;
663
663
let mut alloc_ranges = self . alloc_ranges . borrow_mut ( ) ;
664
664
for ( _, range) in alloc_ranges. iter_mut ( pointer. offset , len) {
665
- if range. read_race_detect ( & * clocks, index) == Err ( DataRace ) {
665
+ if let Err ( DataRace ) = range. read_race_detect ( & * clocks, index) {
666
666
// Report data-race
667
667
return Self :: report_data_race (
668
668
& self . global , range, "READ" , false , pointer, len
@@ -674,18 +674,17 @@ impl VClockAlloc {
674
674
Ok ( ( ) )
675
675
}
676
676
}
677
- /// Detect data-races for an unsychronized write operation, will not perform
678
- /// data-race threads if `multi-threaded` is false, either due to no threads
679
- /// being created or if it is temporarily disabled during a racy read or write
680
- /// operation
681
- pub fn write < ' tcx > ( & mut self , pointer : Pointer < Tag > , len : Size ) -> InterpResult < ' tcx > {
677
+
678
+
679
+ // Shared code for detecting data-races on unique access to a section of memory
680
+ fn unique_access < ' tcx > ( & mut self , pointer : Pointer < Tag > , len : Size , action : & str ) -> InterpResult < ' tcx > {
682
681
if self . global . multi_threaded . get ( ) {
683
682
let ( index, clocks) = self . global . current_thread_state ( ) ;
684
683
for ( _, range) in self . alloc_ranges . get_mut ( ) . iter_mut ( pointer. offset , len) {
685
- if range. write_race_detect ( & * clocks, index) == Err ( DataRace ) {
684
+ if let Err ( DataRace ) = range. write_race_detect ( & * clocks, index) {
686
685
// Report data-race
687
686
return Self :: report_data_race (
688
- & self . global , range, "WRITE" , false , pointer, len
687
+ & self . global , range, action , false , pointer, len
689
688
) ;
690
689
}
691
690
}
@@ -694,25 +693,20 @@ impl VClockAlloc {
694
693
Ok ( ( ) )
695
694
}
696
695
}
696
+
697
+ /// Detect data-races for an unsychronized write operation, will not perform
698
+ /// data-race threads if `multi-threaded` is false, either due to no threads
699
+ /// being created or if it is temporarily disabled during a racy read or write
700
+ /// operation
701
+ pub fn write < ' tcx > ( & mut self , pointer : Pointer < Tag > , len : Size ) -> InterpResult < ' tcx > {
702
+ self . unique_access ( pointer, len, "Write" )
703
+ }
697
704
/// Detect data-races for an unsychronized deallocate operation, will not perform
698
705
/// data-race threads if `multi-threaded` is false, either due to no threads
699
706
/// being created or if it is temporarily disabled during a racy read or write
700
707
/// operation
701
708
pub fn deallocate < ' tcx > ( & mut self , pointer : Pointer < Tag > , len : Size ) -> InterpResult < ' tcx > {
702
- if self . global . multi_threaded . get ( ) {
703
- let ( index, clocks) = self . global . current_thread_state ( ) ;
704
- for ( _, range) in self . alloc_ranges . get_mut ( ) . iter_mut ( pointer. offset , len) {
705
- if range. write_race_detect ( & * clocks, index) == Err ( DataRace ) {
706
- // Report data-race
707
- return Self :: report_data_race (
708
- & self . global , range, "DEALLOCATE" , false , pointer, len
709
- ) ;
710
- }
711
- }
712
- Ok ( ( ) )
713
- } else {
714
- Ok ( ( ) )
715
- }
709
+ self . unique_access ( pointer, len, "Deallocate" )
716
710
}
717
711
}
718
712
@@ -773,6 +767,8 @@ struct ThreadExtraState {
773
767
/// The current vector index in use by the
774
768
/// thread currently, this is set to None
775
769
/// after the vector index has been re-used
770
+ /// and hence the value will never need to be
771
+ /// read during data-race reporting
776
772
vector_index : Option < VectorIdx > ,
777
773
778
774
/// The name of the thread, updated for better
@@ -782,10 +778,8 @@ struct ThreadExtraState {
782
778
783
779
/// Thread termination vector clock, this
784
780
/// is set on thread termination and is used
785
- /// for joining on threads that have already
786
- /// terminated. This should be used first
787
- /// on joining as there is the possibility
788
- /// that `vector_index` is None in some cases
781
+ /// for joining on threads since the vector_index
782
+ /// may be re-used when the join operation occurs
789
783
termination_vector_clock : Option < VClock > ,
790
784
}
791
785
@@ -820,10 +814,26 @@ pub struct GlobalState {
820
814
current_index : Cell < VectorIdx > ,
821
815
822
816
/// Potential vector indices that could be re-used on thread creation
823
- /// values are inserted here on thread termination, vector index values
824
- /// are then re-used once all the termination event happens-before all
825
- /// existing thread-clocks
817
+ /// values are inserted here on after the thread has terminated and
818
+ /// been joined with, and hence may potentially become free
819
+ /// for use as the index for a new thread.
820
+ /// Elements in this set may still require the vector index to
821
+ /// report data-races, and can only be re-used after all
822
+ /// active vector-clocks catch up with the threads timestamp.
826
823
reuse_candidates : RefCell < FxHashSet < VectorIdx > > ,
824
+
825
+ /// Counts the number of threads that are currently active
826
+ /// if the number of active threads reduces to 1 and then
827
+ /// a join operation occures with the remaining main thread
828
+ /// then multi-threaded execution may be disabled
829
+ active_thread_count : Cell < usize > ,
830
+
831
+ /// This contains threads that have terminated, but not yet joined
832
+ /// and so cannot become re-use candidates until a join operation
833
+ /// occurs.
834
+ /// The associated vector index will be moved into re-use candidates
835
+ /// after the join operation occurs
836
+ terminated_threads : RefCell < FxHashMap < ThreadId , VectorIdx > > ,
827
837
}
828
838
impl GlobalState {
829
839
@@ -836,7 +846,9 @@ impl GlobalState {
836
846
vector_info : RefCell :: new ( IndexVec :: new ( ) ) ,
837
847
thread_info : RefCell :: new ( IndexVec :: new ( ) ) ,
838
848
current_index : Cell :: new ( VectorIdx :: new ( 0 ) ) ,
849
+ active_thread_count : Cell :: new ( 1 ) ,
839
850
reuse_candidates : RefCell :: new ( FxHashSet :: default ( ) ) ,
851
+ terminated_threads : RefCell :: new ( FxHashMap :: default ( ) )
840
852
} ;
841
853
842
854
// Setup the main-thread since it is not explicitly created:
@@ -860,10 +872,24 @@ impl GlobalState {
860
872
fn find_vector_index_reuse_candidate ( & self ) -> Option < VectorIdx > {
861
873
let mut reuse = self . reuse_candidates . borrow_mut ( ) ;
862
874
let vector_clocks = self . vector_clocks . borrow ( ) ;
875
+ let vector_info = self . vector_info . borrow ( ) ;
876
+ let terminated_threads = self . terminated_threads . borrow ( ) ;
863
877
for & candidate in reuse. iter ( ) {
864
878
let target_timestamp = vector_clocks[ candidate] . clock [ candidate] ;
865
- if vector_clocks. iter ( ) . all ( |clock| {
866
- clock. clock [ candidate] == target_timestamp
879
+ if vector_clocks. iter_enumerated ( ) . all ( |( clock_idx, clock) | {
880
+ // The thread happens before the clock, and hence cannot report
881
+ // a data-race with this the candidate index
882
+ let no_data_race = clock. clock [ candidate] >= target_timestamp;
883
+
884
+ // The vector represents a thread that has terminated and hence cannot
885
+ // report a data-race with the candidate index
886
+ let thread_id = vector_info[ clock_idx] ;
887
+ let vector_terminated = reuse. contains ( & clock_idx)
888
+ || terminated_threads. contains_key ( & thread_id) ;
889
+
890
+ // The vector index cannot report a race with the candidate index
891
+ // and hence allows the candidate index to be re-used
892
+ no_data_race || vector_terminated
867
893
} ) {
868
894
// All vector clocks for each vector index are equal to
869
895
// the target timestamp, and the thread is known to have
@@ -882,6 +908,10 @@ impl GlobalState {
882
908
pub fn thread_created ( & self , thread : ThreadId ) {
883
909
let current_index = self . current_index ( ) ;
884
910
911
+ // Increment the number of active threads
912
+ let active_threads = self . active_thread_count . get ( ) ;
913
+ self . active_thread_count . set ( active_threads + 1 ) ;
914
+
885
915
// Enable multi-threaded execution, there are now two threads
886
916
// so data-races are now possible.
887
917
self . multi_threaded . set ( true ) ;
@@ -946,51 +976,90 @@ impl GlobalState {
946
976
/// between the joined thead and the current thread.
947
977
#[ inline]
948
978
pub fn thread_joined ( & self , current_thread : ThreadId , join_thread : ThreadId ) {
949
- let ( current_index, join_index) = {
950
- let thread_info = self . thread_info . borrow ( ) ;
951
- let current_index = thread_info[ current_thread] . vector_index
952
- . expect ( "Joining into thread with no assigned vector" ) ;
953
- let join_index = thread_info[ join_thread] . vector_index
954
- . expect ( "Joining thread with no assigned vector" ) ;
955
- ( current_index, join_index)
956
- } ;
957
979
let mut clocks_vec = self . vector_clocks . borrow_mut ( ) ;
958
- let ( current, join) = clocks_vec. pick2_mut ( current_index, join_index) ;
980
+ let thread_info = self . thread_info . borrow ( ) ;
981
+
982
+ // Load the vector clock of the current thread
983
+ let current_index = thread_info[ current_thread] . vector_index
984
+ . expect ( "Performed thread join on thread with no assigned vector" ) ;
985
+ let current = & mut clocks_vec[ current_index] ;
986
+
987
+ // Load the associated vector clock for the terminated thread
988
+ let join_clock = thread_info[ join_thread] . termination_vector_clock
989
+ . as_ref ( ) . expect ( "Joined with thread but thread has not terminated" ) ;
959
990
960
991
// Pre increment clocks before atomic operation
961
992
current. increment_clock ( current_index) ;
962
- join. increment_clock ( join_index) ;
963
993
964
994
// The join thread happens-before the current thread
965
995
// so update the current vector clock
966
- current. join_with ( join) ;
996
+ current. clock . join ( join_clock ) ;
967
997
968
998
// Post increment clocks after atomic operation
969
- // the join clock is not incremented, since there will
970
- // be no future events, also if it was incremented
971
- // the thread re-use condition would never pass
972
999
current. increment_clock ( current_index) ;
1000
+
1001
+ // Check the number of active threads, if the value is 1
1002
+ // then test for potentially disabling multi-threaded execution
1003
+ let active_threads = self . active_thread_count . get ( ) ;
1004
+ if active_threads == 1 {
1005
+ // May potentially be able to disable multi-threaded execution
1006
+ let current_clock = & clocks_vec[ current_index] ;
1007
+ if clocks_vec. iter_enumerated ( ) . all ( |( idx, clocks) | {
1008
+ clocks. clock [ idx] <= current_clock. clock [ idx]
1009
+ } ) {
1010
+ // The all thread termations happen-before the current clock
1011
+ // therefore no data-races can be reported until a new thread
1012
+ // is created, so disable multi-threaded execution
1013
+ self . multi_threaded . set ( false ) ;
1014
+ }
1015
+ }
1016
+
1017
+ // If the thread is marked as terminated but not joined
1018
+ // then move the thread to the re-use set
1019
+ let mut termination = self . terminated_threads . borrow_mut ( ) ;
1020
+ if let Some ( index) = termination. remove ( & join_thread) {
1021
+ let mut reuse = self . reuse_candidates . borrow_mut ( ) ;
1022
+ reuse. insert ( index) ;
1023
+ }
973
1024
}
974
1025
975
1026
/// On thread termination, the vector-clock may re-used
976
1027
/// in the future once all remaining thread-clocks catch
977
- /// up with the time index of the terminated thread
1028
+ /// up with the time index of the terminated thread.
1029
+ /// This assiges thread termination with a unique index
1030
+ /// which will be used to join the thread
1031
+ /// This should be called strictly before any calls to
1032
+ /// `thread_joined`
978
1033
#[ inline]
979
- pub fn thread_terminated ( & self , terminated_thread : ThreadId ) {
980
- let mut thread_info = self . thread_info . borrow_mut ( ) ;
981
- let termination_meta = & mut thread_info[ terminated_thread] ;
1034
+ pub fn thread_terminated ( & self ) {
1035
+ let current_index = self . current_index ( ) ;
1036
+
1037
+ // Increment the clock to a unique termination timestamp
1038
+ let mut vector_clocks = self . vector_clocks . borrow_mut ( ) ;
1039
+ let current_clocks = & mut vector_clocks[ current_index] ;
1040
+ current_clocks. increment_clock ( current_index) ;
982
1041
983
- // Find the terminated index & setup the termination vector-clock
984
- // in case thread join is called in the future after the thread
985
- // has been re-used
986
- let terminated_index = termination_meta. vector_index
987
- . expect ( "Joining into thread with no assigned vector" ) ;
988
- let vector_clocks = self . vector_clocks . borrow ( ) ;
989
- termination_meta. termination_vector_clock = Some ( vector_clocks[ terminated_index] . clock . clone ( ) ) ;
1042
+ // Load the current thread id for the executing vector
1043
+ let vector_info = self . vector_info . borrow ( ) ;
1044
+ let current_thread = vector_info[ current_index] ;
990
1045
991
- // Add this thread as a candidate for re-use
992
- let mut reuse = self . reuse_candidates . borrow_mut ( ) ;
993
- reuse. insert ( terminated_index) ;
1046
+ // Load the current thread metadata, and move to a terminated
1047
+ // vector state. Setting up the vector clock all join operations
1048
+ // will use.
1049
+ let mut thread_info = self . thread_info . borrow_mut ( ) ;
1050
+ let current = & mut thread_info[ current_thread] ;
1051
+ current. termination_vector_clock = Some ( current_clocks. clock . clone ( ) ) ;
1052
+
1053
+ // Add this thread as a candidate for re-use after a thread join
1054
+ // occurs
1055
+ let mut termination = self . terminated_threads . borrow_mut ( ) ;
1056
+ termination. insert ( current_thread, current_index) ;
1057
+
1058
+ // Reduce the number of active threads, now that a thread has
1059
+ // terminated
1060
+ let mut active_threads = self . active_thread_count . get ( ) ;
1061
+ active_threads -= 1 ;
1062
+ self . active_thread_count . set ( active_threads) ;
994
1063
}
995
1064
996
1065
/// Hook for updating the local tracker of the currently
@@ -1118,4 +1187,3 @@ impl GlobalState {
1118
1187
self . current_index . get ( )
1119
1188
}
1120
1189
}
1121
-
0 commit comments