@@ -159,61 +159,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> {
159159 & self , place : MPlaceTy < ' tcx , Tag > , atomic : AtomicReadOp
160160 ) -> InterpResult < ' tcx > {
161161 let this = self . eval_context_ref ( ) ;
162- let data_race = & * this. memory . extra . data_race ;
163- if data_race. multi_threaded . get ( ) {
164-
165- // Load an log the atomic operation
166- // the memory access has to be `get_raw` since otherwise this despite only
167- // mutating MemoryExtra will still trigger errors on read-only memory
168- let place_ptr = place. ptr . assert_ptr ( ) ;
169- let size = place. layout . size ;
170- let alloc_meta = & this. memory . get_raw ( place_ptr. alloc_id ) ?. extra . data_race ;
171- log:: trace!(
172- "Atomic op({}) with ordering {:?} on memory({:?}, offset={}, size={})" ,
173- "Atomic load" , & atomic, place_ptr. alloc_id, place_ptr. offset. bytes( ) , size. bytes( )
174- ) ;
175-
176- // Perform the atomic operation
177- let data_race = & alloc_meta. global ;
178- data_race. maybe_perform_sync_operation ( move |index, mut clocks| {
179- for ( _, range) in alloc_meta. alloc_ranges . borrow_mut ( ) . iter_mut ( place_ptr. offset , size) {
180- let res = if atomic == AtomicReadOp :: Relaxed {
181- range. load_relaxed ( & mut * clocks, index)
182- } else {
183- range. acquire ( & mut * clocks, index)
184- } ;
185- if let Err ( DataRace ) = res {
186- mem:: drop ( clocks) ;
187- return VClockAlloc :: report_data_race (
188- & alloc_meta. global , range, "Atomic load" , true ,
189- place_ptr, size
190- ) ;
191- }
192- }
193- Ok ( ( ) )
194- } ) ?;
195-
196- // Log changes to atomic memory
197- if log:: log_enabled!( log:: Level :: Trace ) {
198- for ( _, range) in alloc_meta. alloc_ranges . borrow ( ) . iter ( place_ptr. offset , size) {
199- log:: trace!(
200- "Updated atomic memory({:?}, offset={}, size={}) to {:#?}" ,
201- place. ptr. assert_ptr( ) . alloc_id, place_ptr. offset. bytes( ) , size. bytes( ) ,
202- range. atomic_ops
203- ) ;
162+ this. validate_atomic_op (
163+ place, atomic, "Atomic Load" ,
164+ move |memory, clocks, index, atomic| {
165+ if atomic == AtomicReadOp :: Relaxed {
166+ memory. load_relaxed ( & mut * clocks, index)
167+ } else {
168+ memory. acquire ( & mut * clocks, index)
204169 }
205170 }
206- }
207- Ok ( ( ) )
171+ )
208172 }
209173
210174 /// Update the data-race detector for an atomic write occuring at the
211175 /// associated memory-place and on the current thread
212176 fn validate_atomic_store (
213177 & mut self , place : MPlaceTy < ' tcx , Tag > , atomic : AtomicWriteOp
214178 ) -> InterpResult < ' tcx > {
215- let this = self . eval_context_mut ( ) ;
216- this. validate_atomic_op_mut (
179+ let this = self . eval_context_ref ( ) ;
180+ this. validate_atomic_op (
217181 place, atomic, "Atomic Store" ,
218182 move |memory, clocks, index, atomic| {
219183 if atomic == AtomicWriteOp :: Relaxed {
@@ -233,8 +197,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> {
233197 use AtomicRWOp :: * ;
234198 let acquire = matches ! ( atomic, Acquire | AcqRel | SeqCst ) ;
235199 let release = matches ! ( atomic, Release | AcqRel | SeqCst ) ;
236- let this = self . eval_context_mut ( ) ;
237- this. validate_atomic_op_mut (
200+ let this = self . eval_context_ref ( ) ;
201+ this. validate_atomic_op (
238202 place, atomic, "Atomic RMW" ,
239203 move |memory, clocks, index, _| {
240204 if acquire {
@@ -276,25 +240,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> {
276240impl < ' mir , ' tcx : ' mir > EvalContextPrivExt < ' mir , ' tcx > for MiriEvalContext < ' mir , ' tcx > { }
277241trait EvalContextPrivExt < ' mir , ' tcx : ' mir > : MiriEvalContextExt < ' mir , ' tcx > {
278242
279- /// Generic atomic operation implementation, this however
280- /// cannot be used for the atomic read operation since
281- /// that requires non mutable memory access to not trigger
282- /// the writing to read-only memory errors during `get_raw_mut`
283- fn validate_atomic_op_mut < A : Debug + Copy > (
284- & mut self , place : MPlaceTy < ' tcx , Tag > ,
243+ /// Generic atomic operation implementation,
244+ /// this accesses memory via get_raw instead of
245+ /// get_raw_mut, due to issues calling get_raw_mut
246+ /// for atomic loads from read-only memory
247+ /// FIXME: is this valid, or should get_raw_mut be used for
248+ /// atomic-stores/atomic-rmw?
249+ fn validate_atomic_op < A : Debug + Copy > (
250+ & self , place : MPlaceTy < ' tcx , Tag > ,
285251 atomic : A , description : & str ,
286252 mut op : impl FnMut (
287253 & mut MemoryCellClocks , & mut ThreadClockSet , VectorIdx , A
288254 ) -> Result < ( ) , DataRace >
289255 ) -> InterpResult < ' tcx > {
290- let this = self . eval_context_mut ( ) ;
256+ let this = self . eval_context_ref ( ) ;
291257 let data_race = & * this. memory . extra . data_race ;
292258 if data_race. multi_threaded . get ( ) {
293259
294260 // Load an log the atomic operation
295261 let place_ptr = place. ptr . assert_ptr ( ) ;
296262 let size = place. layout . size ;
297- let alloc_meta = & mut this. memory . get_raw_mut ( place_ptr. alloc_id ) ?. extra . data_race ;
263+ let alloc_meta = & this. memory . get_raw ( place_ptr. alloc_id ) ?. extra . data_race ;
298264 log:: trace!(
299265 "Atomic op({}) with ordering {:?} on memory({:?}, offset={}, size={})" ,
300266 description, & atomic, place_ptr. alloc_id, place_ptr. offset. bytes( ) , size. bytes( )
@@ -800,6 +766,29 @@ impl ThreadClockSet {
800766 }
801767}
802768
769+ /// Extra metadata associated with a thread
770+ #[ derive( Debug , Clone , Default ) ]
771+ struct ThreadExtraState {
772+
773+ /// The current vector index in use by the
774+ /// thread currently, this is set to None
775+ /// after the vector index has been re-used
776+ vector_index : Option < VectorIdx > ,
777+
778+ /// The name of the thread, updated for better
779+ /// diagnostics when reporting detected data
780+ /// races
781+ thread_name : Option < Box < str > > ,
782+
783+ /// Thread termination vector clock, this
784+ /// 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
789+ termination_vector_clock : Option < VClock > ,
790+ }
791+
803792/// Global data-race detection state, contains the currently
804793/// executing thread as well as the vector-clocks associated
805794/// with each of the threads.
@@ -822,18 +811,18 @@ pub struct GlobalState {
822811 /// Mapping of a given vector index to the current thread
823812 /// that the execution is representing, this may change
824813 /// if a vector index is re-assigned to a new thread
825- vector_info : RefCell < IndexVec < VectorIdx , ThreadId > > , //FIXME: make option
814+ vector_info : RefCell < IndexVec < VectorIdx , ThreadId > > ,
826815
827- /// The mapping of a given thread to a known vector clock
828- thread_info : RefCell < IndexVec < ThreadId , ( Option < VectorIdx > , Option < Box < str > > ) > > ,
816+ /// The mapping of a given thread to assocaited thread metadata
817+ thread_info : RefCell < IndexVec < ThreadId , ThreadExtraState > > ,
829818
830819 /// The current vector index being executed
831820 current_index : Cell < VectorIdx > ,
832821
833822 /// Potential vector indices that could be re-used on thread creation
834- /// values are inserted here on thread join events, and can be
835- /// re-used once the vector clocks of all current threads
836- /// are equal to the vector clock of the joined thread
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
837826 reuse_candidates : RefCell < FxHashSet < VectorIdx > > ,
838827}
839828impl GlobalState {
@@ -856,8 +845,12 @@ impl GlobalState {
856845 let index = global_state. vector_clocks . borrow_mut ( ) . push ( ThreadClockSet :: default ( ) ) ;
857846 global_state. vector_info . borrow_mut ( ) . push ( ThreadId :: new ( 0 ) ) ;
858847 global_state. thread_info . borrow_mut ( ) . push (
859- ( Some ( index) , Some ( "main" . to_string ( ) . into_boxed_str ( ) )
860- ) ) ;
848+ ThreadExtraState {
849+ vector_index : Some ( index) ,
850+ thread_name : Some ( "main" . to_string ( ) . into_boxed_str ( ) ) ,
851+ termination_vector_clock : None
852+ }
853+ ) ;
861854
862855 global_state
863856 }
@@ -873,10 +866,9 @@ impl GlobalState {
873866 clock. clock [ candidate] == target_timestamp
874867 } ) {
875868 // All vector clocks for each vector index are equal to
876- // the target timestamp, therefore since the thread has
877- // terminated and cannot update the vector clock.
878- // No more data-races involving this vector index are possible
879- // so it can be re-used
869+ // the target timestamp, and the thread is known to have
870+ // terminated, therefore this vector clock index cannot
871+ // report any more data-races
880872 assert ! ( reuse. remove( & candidate) ) ;
881873 return Some ( candidate)
882874 }
@@ -916,7 +908,7 @@ impl GlobalState {
916908
917909 // Mark the thread the vector index was associated with as no longer
918910 // representing a thread index
919- thread_info[ old_thread] . 0 = None ;
911+ thread_info[ old_thread] . vector_index = None ;
920912
921913 reuse_index
922914 } else {
@@ -927,7 +919,7 @@ impl GlobalState {
927919 } ;
928920
929921 // Mark the chosen vector index as in use by the thread
930- thread_info[ thread] . 0 = Some ( created_index) ;
922+ thread_info[ thread] . vector_index = Some ( created_index) ;
931923
932924 // Create a thread clock set if applicable
933925 let mut vector_clocks = self . vector_clocks . borrow_mut ( ) ;
@@ -952,15 +944,13 @@ impl GlobalState {
952944
953945 /// Hook on a thread join to update the implicit happens-before relation
954946 /// between the joined thead and the current thread.
955- /// Called after the join has occured, and hence implicitly also states
956- /// that the thread must have terminated as well
957947 #[ inline]
958948 pub fn thread_joined ( & self , current_thread : ThreadId , join_thread : ThreadId ) {
959949 let ( current_index, join_index) = {
960950 let thread_info = self . thread_info . borrow ( ) ;
961- let current_index = thread_info[ current_thread] . 0
951+ let current_index = thread_info[ current_thread] . vector_index
962952 . expect ( "Joining into thread with no assigned vector" ) ;
963- let join_index = thread_info[ join_thread] . 0
953+ let join_index = thread_info[ join_thread] . vector_index
964954 . expect ( "Joining thread with no assigned vector" ) ;
965955 ( current_index, join_index)
966956 } ;
@@ -976,16 +966,31 @@ impl GlobalState {
976966 current. join_with ( join) ;
977967
978968 // 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
979972 current. increment_clock ( current_index) ;
980- join. increment_clock ( join_index) ;
973+ }
974+
975+ /// On thread termination, the vector-clock may re-used
976+ /// in the future once all remaining thread-clocks catch
977+ /// up with the time index of the terminated thread
978+ #[ 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] ;
982+
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 ( ) ) ;
981990
982- // The joined thread vector clock is a potential candidate
983- // for re-use given sufficient time, mark as available once
984- // threads have been created. This is because this function
985- // is called once join_thread has terminated and such cannot
986- // update any-more
991+ // Add this thread as a candidate for re-use
987992 let mut reuse = self . reuse_candidates . borrow_mut ( ) ;
988- reuse. insert ( join_index ) ;
993+ reuse. insert ( terminated_index ) ;
989994 }
990995
991996 /// Hook for updating the local tracker of the currently
@@ -994,7 +999,7 @@ impl GlobalState {
994999 #[ inline]
9951000 pub fn thread_set_active ( & self , thread : ThreadId ) {
9961001 let thread_info = self . thread_info . borrow ( ) ;
997- let vector_idx = thread_info[ thread] . 0
1002+ let vector_idx = thread_info[ thread] . vector_index
9981003 . expect ( "Setting thread active with no assigned vector" ) ;
9991004 self . current_index . set ( vector_idx) ;
10001005 }
@@ -1007,7 +1012,7 @@ impl GlobalState {
10071012 pub fn thread_set_name ( & self , thread : ThreadId , name : String ) {
10081013 let name = name. into_boxed_str ( ) ;
10091014 let mut thread_info = self . thread_info . borrow_mut ( ) ;
1010- thread_info[ thread] . 1 = Some ( name) ;
1015+ thread_info[ thread] . thread_name = Some ( name) ;
10111016 }
10121017
10131018
@@ -1036,7 +1041,7 @@ impl GlobalState {
10361041 /// returns the id and the name for better diagnostics
10371042 fn print_thread_metadata ( & self , vector : VectorIdx ) -> String {
10381043 let thread = self . vector_info . borrow ( ) [ vector] ;
1039- let thread_name = & self . thread_info . borrow ( ) [ thread] . 1 ;
1044+ let thread_name = & self . thread_info . borrow ( ) [ thread] . thread_name ;
10401045 if let Some ( name) = thread_name {
10411046 let name: & str = name;
10421047 format ! ( "Thread(id = {:?}, name = {:?})" , thread. to_u32( ) , & * name)
@@ -1079,7 +1084,7 @@ impl GlobalState {
10791084 /// used by the thread
10801085 #[ inline]
10811086 fn load_thread_state_mut ( & self , thread : ThreadId ) -> ( VectorIdx , RefMut < ' _ , ThreadClockSet > ) {
1082- let index = self . thread_info . borrow ( ) [ thread] . 0
1087+ let index = self . thread_info . borrow ( ) [ thread] . vector_index
10831088 . expect ( "Loading thread state for thread with no assigned vector" ) ;
10841089 let ref_vector = self . vector_clocks . borrow_mut ( ) ;
10851090 let clocks = RefMut :: map ( ref_vector, |vec| & mut vec[ index] ) ;
0 commit comments