@@ -21,7 +21,7 @@ use crate::shims::tls;
2121use crate :: * ;
2222
2323#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
24- pub enum SchedulingAction {
24+ enum SchedulingAction {
2525 /// Execute step on the active thread.
2626 ExecuteStep ,
2727 /// Execute a timeout callback.
@@ -450,8 +450,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
450450 }
451451
452452 /// 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 > {
455454 & mut self . threads [ self . active_thread ]
456455 }
457456
@@ -718,6 +717,51 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
718717 }
719718}
720719
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+
721765// Public interface to thread management.
722766impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriInterpCx < ' mir , ' tcx > { }
723767pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriInterpCxExt < ' mir , ' tcx > {
@@ -961,61 +1005,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
9611005 this. machine . threads . unregister_timeout_callback_if_exists ( thread) ;
9621006 }
9631007
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 , ! > {
9671011 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+ }
10111031 }
10121032
10131033 /// Handles thread termination of the active thread: wakes up threads joining on this one,
10141034 /// and deallocated thread-local statics.
10151035 ///
10161036 /// This is called by the eval loop when a thread's on_stack_empty returns `Ready`.
10171037 #[ inline]
1018- fn thread_terminated ( & mut self ) -> InterpResult < ' tcx > {
1038+ fn terminate_active_thread ( & mut self ) -> InterpResult < ' tcx > {
10191039 let this = self . eval_context_mut ( ) ;
10201040 let thread = this. active_thread_mut ( ) ;
10211041 assert ! ( thread. stack. is_empty( ) , "only threads with an empty stack can be terminated" ) ;
0 commit comments