@@ -6,6 +6,8 @@ use std::task::Poll;
66use std:: time:: { Duration , SystemTime } ;
77
88use either:: Either ;
9+ use rand:: rngs:: StdRng ;
10+ use rand:: seq:: IteratorRandom ;
911use rustc_abi:: ExternAbi ;
1012use rustc_const_eval:: CTRL_C_RECEIVED ;
1113use rustc_data_structures:: fx:: FxHashMap ;
@@ -401,6 +403,8 @@ pub struct ThreadManager<'tcx> {
401403 thread_local_allocs : FxHashMap < ( DefId , ThreadId ) , StrictPointer > ,
402404 /// A flag that indicates that we should change the active thread.
403405 yield_active_thread : bool ,
406+ /// A flag that indicates that we should do round robin scheduling of threads else randomized scheduling is used.
407+ fixed_scheduling : bool ,
404408}
405409
406410impl VisitProvenance for ThreadManager < ' _ > {
@@ -410,6 +414,7 @@ impl VisitProvenance for ThreadManager<'_> {
410414 thread_local_allocs,
411415 active_thread : _,
412416 yield_active_thread : _,
417+ fixed_scheduling : _,
413418 } = self ;
414419
415420 for thread in threads {
@@ -421,8 +426,8 @@ impl VisitProvenance for ThreadManager<'_> {
421426 }
422427}
423428
424- impl < ' tcx > Default for ThreadManager < ' tcx > {
425- fn default ( ) -> Self {
429+ impl < ' tcx > ThreadManager < ' tcx > {
430+ pub ( crate ) fn new ( config : & MiriConfig ) -> Self {
426431 let mut threads = IndexVec :: new ( ) ;
427432 // Create the main thread and add it to the list of threads.
428433 threads. push ( Thread :: new ( Some ( "main" ) , None ) ) ;
@@ -431,11 +436,10 @@ impl<'tcx> Default for ThreadManager<'tcx> {
431436 threads,
432437 thread_local_allocs : Default :: default ( ) ,
433438 yield_active_thread : false ,
439+ fixed_scheduling : config. fixed_scheduling ,
434440 }
435441 }
436- }
437442
438- impl < ' tcx > ThreadManager < ' tcx > {
439443 pub ( crate ) fn init (
440444 ecx : & mut MiriInterpCx < ' tcx > ,
441445 on_main_stack_empty : StackEmptyCallback < ' tcx > ,
@@ -702,7 +706,11 @@ impl<'tcx> ThreadManager<'tcx> {
702706 /// used in stateless model checkers such as Loom: run the active thread as
703707 /// long as we can and switch only when we have to (the active thread was
704708 /// blocked, terminated, or has explicitly asked to be preempted).
705- fn schedule ( & mut self , clock : & MonotonicClock ) -> InterpResult < ' tcx , SchedulingAction > {
709+ fn schedule (
710+ & mut self ,
711+ clock : & MonotonicClock ,
712+ rng : & mut StdRng ,
713+ ) -> InterpResult < ' tcx , SchedulingAction > {
706714 // This thread and the program can keep going.
707715 if self . threads [ self . active_thread ] . state . is_enabled ( ) && !self . yield_active_thread {
708716 // The currently active thread is still enabled, just continue with it.
@@ -720,30 +728,33 @@ impl<'tcx> ThreadManager<'tcx> {
720728 }
721729 // No callbacks immediately scheduled, pick a regular thread to execute.
722730 // The active thread blocked or yielded. So we go search for another enabled thread.
723- // Crucially, we start searching at the current active thread ID, rather than at 0, since we
724- // want to avoid always scheduling threads 0 and 1 without ever making progress in thread 2.
725- //
726- // `skip(N)` means we start iterating at thread N, so we skip 1 more to start just *after*
727- // the active thread. Then after that we look at `take(N)`, i.e., the threads *before* the
728- // active thread.
729- let threads = self
731+ // We build the list of threads by starting with the threads after the current one, followed by
732+ // the threads before the current one and then the current thread itself (i.e., this iterator acts
733+ // like `threads.rotate_left(self.active_thread.index() + 1)`. This ensures that if we pick the first
734+ // eligible thread, we do regular round-robin scheduling, and all threads get a chance to take a step.
735+ let mut threads_iter = self
730736 . threads
731737 . iter_enumerated ( )
732738 . skip ( self . active_thread . index ( ) + 1 )
733- . chain ( self . threads . iter_enumerated ( ) . take ( self . active_thread . index ( ) ) ) ;
734- for ( id, thread) in threads {
735- debug_assert_ne ! ( self . active_thread, id) ;
736- if thread. state . is_enabled ( ) {
739+ . chain ( self . threads . iter_enumerated ( ) . take ( self . active_thread . index ( ) + 1 ) )
740+ . filter ( |( _id, thread) | thread. state . is_enabled ( ) ) ;
741+ // Pick a new thread, and switch to it.
742+ let new_thread =
743+ if self . fixed_scheduling { threads_iter. next ( ) } else { threads_iter. choose ( rng) } ;
744+
745+ if let Some ( ( id, _thread) ) = new_thread {
746+ if self . active_thread != id {
737747 info ! (
738748 "---------- Now executing on thread `{}` (previous: `{}`) ----------------------------------------" ,
739749 self . get_thread_display_name( id) ,
740750 self . get_thread_display_name( self . active_thread)
741751 ) ;
742752 self . active_thread = id;
743- break ;
744753 }
745754 }
755+ // This completes the `yield`, if any was requested.
746756 self . yield_active_thread = false ;
757+
747758 if self . threads [ self . active_thread ] . state . is_enabled ( ) {
748759 return interp_ok ( SchedulingAction :: ExecuteStep ) ;
749760 }
@@ -1138,7 +1149,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11381149 use rand:: Rng as _;
11391150
11401151 let this = self . eval_context_mut ( ) ;
1141- if this. machine . rng . get_mut ( ) . random_bool ( this. machine . preemption_rate ) {
1152+ if !this. machine . threads . fixed_scheduling
1153+ && this. machine . rng . get_mut ( ) . random_bool ( this. machine . preemption_rate )
1154+ {
11421155 this. yield_active_thread ( ) ;
11431156 }
11441157 }
@@ -1152,7 +1165,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11521165 this. machine . handle_abnormal_termination ( ) ;
11531166 throw_machine_stop ! ( TerminationInfo :: Interrupted ) ;
11541167 }
1155- match this. machine . threads . schedule ( & this. machine . monotonic_clock ) ? {
1168+ let rng = this. machine . rng . get_mut ( ) ;
1169+ match this. machine . threads . schedule ( & this. machine . monotonic_clock , rng) ? {
11561170 SchedulingAction :: ExecuteStep => {
11571171 if !this. step ( ) ? {
11581172 // See if this thread can do something else.
0 commit comments