@@ -6,6 +6,8 @@ use std::task::Poll;
6
6
use std:: time:: { Duration , SystemTime } ;
7
7
8
8
use either:: Either ;
9
+ use rand:: rngs:: StdRng ;
10
+ use rand:: seq:: IteratorRandom ;
9
11
use rustc_abi:: ExternAbi ;
10
12
use rustc_const_eval:: CTRL_C_RECEIVED ;
11
13
use rustc_data_structures:: fx:: FxHashMap ;
@@ -401,6 +403,8 @@ pub struct ThreadManager<'tcx> {
401
403
thread_local_allocs : FxHashMap < ( DefId , ThreadId ) , StrictPointer > ,
402
404
/// A flag that indicates that we should change the active thread.
403
405
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 ,
404
408
}
405
409
406
410
impl VisitProvenance for ThreadManager < ' _ > {
@@ -410,6 +414,7 @@ impl VisitProvenance for ThreadManager<'_> {
410
414
thread_local_allocs,
411
415
active_thread : _,
412
416
yield_active_thread : _,
417
+ fixed_scheduling : _,
413
418
} = self ;
414
419
415
420
for thread in threads {
@@ -421,8 +426,8 @@ impl VisitProvenance for ThreadManager<'_> {
421
426
}
422
427
}
423
428
424
- impl < ' tcx > Default for ThreadManager < ' tcx > {
425
- fn default ( ) -> Self {
429
+ impl < ' tcx > ThreadManager < ' tcx > {
430
+ pub ( crate ) fn new ( config : & MiriConfig ) -> Self {
426
431
let mut threads = IndexVec :: new ( ) ;
427
432
// Create the main thread and add it to the list of threads.
428
433
threads. push ( Thread :: new ( Some ( "main" ) , None ) ) ;
@@ -431,11 +436,10 @@ impl<'tcx> Default for ThreadManager<'tcx> {
431
436
threads,
432
437
thread_local_allocs : Default :: default ( ) ,
433
438
yield_active_thread : false ,
439
+ fixed_scheduling : config. fixed_scheduling ,
434
440
}
435
441
}
436
- }
437
442
438
- impl < ' tcx > ThreadManager < ' tcx > {
439
443
pub ( crate ) fn init (
440
444
ecx : & mut MiriInterpCx < ' tcx > ,
441
445
on_main_stack_empty : StackEmptyCallback < ' tcx > ,
@@ -702,7 +706,11 @@ impl<'tcx> ThreadManager<'tcx> {
702
706
/// used in stateless model checkers such as Loom: run the active thread as
703
707
/// long as we can and switch only when we have to (the active thread was
704
708
/// 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 > {
706
714
// This thread and the program can keep going.
707
715
if self . threads [ self . active_thread ] . state . is_enabled ( ) && !self . yield_active_thread {
708
716
// The currently active thread is still enabled, just continue with it.
@@ -720,30 +728,33 @@ impl<'tcx> ThreadManager<'tcx> {
720
728
}
721
729
// No callbacks immediately scheduled, pick a regular thread to execute.
722
730
// 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
730
736
. threads
731
737
. iter_enumerated ( )
732
738
. 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 {
737
747
info ! (
738
748
"---------- Now executing on thread `{}` (previous: `{}`) ----------------------------------------" ,
739
749
self . get_thread_display_name( id) ,
740
750
self . get_thread_display_name( self . active_thread)
741
751
) ;
742
752
self . active_thread = id;
743
- break ;
744
753
}
745
754
}
755
+ // This completes the `yield`, if any was requested.
746
756
self . yield_active_thread = false ;
757
+
747
758
if self . threads [ self . active_thread ] . state . is_enabled ( ) {
748
759
return interp_ok ( SchedulingAction :: ExecuteStep ) ;
749
760
}
@@ -1138,7 +1149,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1138
1149
use rand:: Rng as _;
1139
1150
1140
1151
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
+ {
1142
1155
this. yield_active_thread ( ) ;
1143
1156
}
1144
1157
}
@@ -1152,7 +1165,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1152
1165
this. machine . handle_abnormal_termination ( ) ;
1153
1166
throw_machine_stop ! ( TerminationInfo :: Interrupted ) ;
1154
1167
}
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) ? {
1156
1170
SchedulingAction :: ExecuteStep => {
1157
1171
if !this. step ( ) ? {
1158
1172
// See if this thread can do something else.
0 commit comments