@@ -21,7 +21,7 @@ use crate::shims::tls;
21
21
use crate :: * ;
22
22
23
23
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
24
- pub enum SchedulingAction {
24
+ enum SchedulingAction {
25
25
/// Execute step on the active thread.
26
26
ExecuteStep ,
27
27
/// Execute a timeout callback.
@@ -450,8 +450,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
450
450
}
451
451
452
452
/// Get a mutable borrow of the currently active thread.
453
- /// (Private for a bit of protection.)
454
- fn active_thread_mut ( & mut self ) -> & mut Thread < ' mir , ' tcx > {
453
+ pub fn active_thread_mut ( & mut self ) -> & mut Thread < ' mir , ' tcx > {
455
454
& mut self . threads [ self . active_thread ]
456
455
}
457
456
@@ -718,6 +717,51 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
718
717
}
719
718
}
720
719
720
+ impl < ' mir , ' tcx : ' mir > EvalContextPrivExt < ' mir , ' tcx > for MiriInterpCx < ' mir , ' tcx > { }
721
+ trait EvalContextPrivExt < ' mir , ' tcx : ' mir > : MiriInterpCxExt < ' mir , ' tcx > {
722
+ /// Execute a timeout callback on the callback's thread.
723
+ #[ inline]
724
+ fn run_timeout_callback ( & mut self ) -> InterpResult < ' tcx > {
725
+ let this = self . eval_context_mut ( ) ;
726
+ let ( thread, callback) = if let Some ( ( thread, callback) ) =
727
+ this. machine . threads . get_ready_callback ( & this. machine . clock )
728
+ {
729
+ ( thread, callback)
730
+ } else {
731
+ // get_ready_callback can return None if the computer's clock
732
+ // was shifted after calling the scheduler and before the call
733
+ // to get_ready_callback (see issue
734
+ // https://github.com/rust-lang/miri/issues/1763). In this case,
735
+ // just do nothing, which effectively just returns to the
736
+ // scheduler.
737
+ return Ok ( ( ) ) ;
738
+ } ;
739
+ // This back-and-forth with `set_active_thread` is here because of two
740
+ // design decisions:
741
+ // 1. Make the caller and not the callback responsible for changing
742
+ // thread.
743
+ // 2. Make the scheduler the only place that can change the active
744
+ // thread.
745
+ let old_thread = this. set_active_thread ( thread) ;
746
+ callback. call ( this) ?;
747
+ this. set_active_thread ( old_thread) ;
748
+ Ok ( ( ) )
749
+ }
750
+
751
+ #[ inline]
752
+ fn run_on_stack_empty ( & mut self ) -> InterpResult < ' tcx , Poll < ( ) > > {
753
+ let this = self . eval_context_mut ( ) ;
754
+ let mut callback = this
755
+ . active_thread_mut ( )
756
+ . on_stack_empty
757
+ . take ( )
758
+ . expect ( "`on_stack_empty` not set up, or already running" ) ;
759
+ let res = callback ( this) ?;
760
+ this. active_thread_mut ( ) . on_stack_empty = Some ( callback) ;
761
+ Ok ( res)
762
+ }
763
+ }
764
+
721
765
// Public interface to thread management.
722
766
impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
723
767
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
@@ -961,61 +1005,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
961
1005
this. machine . threads . unregister_timeout_callback_if_exists ( thread) ;
962
1006
}
963
1007
964
- /// Execute a timeout callback on the callback's thread.
965
- # [ inline ]
966
- fn run_timeout_callback ( & mut self ) -> InterpResult < ' tcx > {
1008
+ /// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program
1009
+ /// termination).
1010
+ fn run_threads ( & mut self ) -> InterpResult < ' tcx , ! > {
967
1011
let this = self . eval_context_mut ( ) ;
968
- let ( thread, callback) = if let Some ( ( thread, callback) ) =
969
- this. machine . threads . get_ready_callback ( & this. machine . clock )
970
- {
971
- ( thread, callback)
972
- } else {
973
- // get_ready_callback can return None if the computer's clock
974
- // was shifted after calling the scheduler and before the call
975
- // to get_ready_callback (see issue
976
- // https://github.com/rust-lang/miri/issues/1763). In this case,
977
- // just do nothing, which effectively just returns to the
978
- // scheduler.
979
- return Ok ( ( ) ) ;
980
- } ;
981
- // This back-and-forth with `set_active_thread` is here because of two
982
- // design decisions:
983
- // 1. Make the caller and not the callback responsible for changing
984
- // thread.
985
- // 2. Make the scheduler the only place that can change the active
986
- // thread.
987
- let old_thread = this. set_active_thread ( thread) ;
988
- callback. call ( this) ?;
989
- this. set_active_thread ( old_thread) ;
990
- Ok ( ( ) )
991
- }
992
-
993
- #[ inline]
994
- fn run_on_stack_empty ( & mut self ) -> InterpResult < ' tcx , Poll < ( ) > > {
995
- let this = self . eval_context_mut ( ) ;
996
- let mut callback = this
997
- . active_thread_mut ( )
998
- . on_stack_empty
999
- . take ( )
1000
- . expect ( "`on_stack_empty` not set up, or already running" ) ;
1001
- let res = callback ( this) ?;
1002
- this. active_thread_mut ( ) . on_stack_empty = Some ( callback) ;
1003
- Ok ( res)
1004
- }
1005
-
1006
- /// Decide which action to take next and on which thread.
1007
- #[ inline]
1008
- fn schedule ( & mut self ) -> InterpResult < ' tcx , SchedulingAction > {
1009
- let this = self . eval_context_mut ( ) ;
1010
- this. machine . threads . schedule ( & this. machine . clock )
1012
+ loop {
1013
+ match this. machine . threads . schedule ( & this. machine . clock ) ? {
1014
+ SchedulingAction :: ExecuteStep => {
1015
+ if !this. step ( ) ? {
1016
+ // See if this thread can do something else.
1017
+ match this. run_on_stack_empty ( ) ? {
1018
+ Poll :: Pending => { } // keep going
1019
+ Poll :: Ready ( ( ) ) => this. terminate_active_thread ( ) ?,
1020
+ }
1021
+ }
1022
+ }
1023
+ SchedulingAction :: ExecuteTimeoutCallback => {
1024
+ this. run_timeout_callback ( ) ?;
1025
+ }
1026
+ SchedulingAction :: Sleep ( duration) => {
1027
+ this. machine . clock . sleep ( duration) ;
1028
+ }
1029
+ }
1030
+ }
1011
1031
}
1012
1032
1013
1033
/// Handles thread termination of the active thread: wakes up threads joining on this one,
1014
1034
/// and deallocated thread-local statics.
1015
1035
///
1016
1036
/// This is called by the eval loop when a thread's on_stack_empty returns `Ready`.
1017
1037
#[ inline]
1018
- fn thread_terminated ( & mut self ) -> InterpResult < ' tcx > {
1038
+ fn terminate_active_thread ( & mut self ) -> InterpResult < ' tcx > {
1019
1039
let this = self . eval_context_mut ( ) ;
1020
1040
let thread = this. active_thread_mut ( ) ;
1021
1041
assert ! ( thread. stack. is_empty( ) , "only threads with an empty stack can be terminated" ) ;
0 commit comments