3
3
use std:: cell:: RefCell ;
4
4
use std:: collections:: hash_map:: Entry ;
5
5
use std:: num:: TryFromIntError ;
6
+ use std:: task:: Poll ;
6
7
use std:: time:: { Duration , SystemTime } ;
7
8
8
9
use log:: trace;
@@ -16,6 +17,7 @@ use rustc_target::spec::abi::Abi;
16
17
17
18
use crate :: concurrency:: data_race;
18
19
use crate :: concurrency:: sync:: SynchronizationState ;
20
+ use crate :: shims:: tls;
19
21
use crate :: * ;
20
22
21
23
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
@@ -24,10 +26,8 @@ pub enum SchedulingAction {
24
26
ExecuteStep ,
25
27
/// Execute a timeout callback.
26
28
ExecuteTimeoutCallback ,
27
- /// Execute destructors of the active thread.
28
- ExecuteDtors ,
29
- /// Stop the program.
30
- Stop ,
29
+ /// Wait for a bit, until there is a timeout to be called.
30
+ Sleep ( Duration ) ,
31
31
}
32
32
33
33
/// Trait for callbacks that can be executed when some event happens, such as after a timeout.
@@ -41,9 +41,6 @@ type TimeoutCallback<'mir, 'tcx> = Box<dyn MachineCallback<'mir, 'tcx> + 'tcx>;
41
41
#[ derive( Clone , Copy , Debug , PartialOrd , Ord , PartialEq , Eq , Hash ) ]
42
42
pub struct ThreadId ( u32 ) ;
43
43
44
- /// The main thread. When it terminates, the whole application terminates.
45
- const MAIN_THREAD : ThreadId = ThreadId ( 0 ) ;
46
-
47
44
impl ThreadId {
48
45
pub fn to_u32 ( self ) -> u32 {
49
46
self . 0
@@ -118,6 +115,12 @@ pub struct Thread<'mir, 'tcx> {
118
115
/// The virtual call stack.
119
116
stack : Vec < Frame < ' mir , ' tcx , Provenance , FrameData < ' tcx > > > ,
120
117
118
+ /// The function to call when the stack ran empty, to figure out what to do next.
119
+ /// Conceptually, this is the interpreter implementation of the things that happen 'after' the
120
+ /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime).
121
+ /// (`None` is an error, it means the callback has not been set up yet or is actively running.)
122
+ pub ( crate ) on_stack_empty : Option < StackEmptyCallback < ' mir , ' tcx > > ,
123
+
121
124
/// The index of the topmost user-relevant frame in `stack`. This field must contain
122
125
/// the value produced by `get_top_user_relevant_frame`.
123
126
/// The `None` state here represents
@@ -137,19 +140,10 @@ pub struct Thread<'mir, 'tcx> {
137
140
pub ( crate ) last_error : Option < MPlaceTy < ' tcx , Provenance > > ,
138
141
}
139
142
140
- impl < ' mir , ' tcx > Thread < ' mir , ' tcx > {
141
- /// Check if the thread is done executing (no more stack frames). If yes,
142
- /// change the state to terminated and return `true`.
143
- fn check_terminated ( & mut self ) -> bool {
144
- if self . state == ThreadState :: Enabled {
145
- if self . stack . is_empty ( ) {
146
- self . state = ThreadState :: Terminated ;
147
- return true ;
148
- }
149
- }
150
- false
151
- }
143
+ pub type StackEmptyCallback < ' mir , ' tcx > =
144
+ Box < dyn FnMut ( & mut MiriInterpCx < ' mir , ' tcx > ) -> InterpResult < ' tcx , Poll < ( ) > > > ;
152
145
146
+ impl < ' mir , ' tcx > Thread < ' mir , ' tcx > {
153
147
/// Get the name of the current thread, or `<unnamed>` if it was not set.
154
148
fn thread_name ( & self ) -> & [ u8 ] {
155
149
if let Some ( ref thread_name) = self . thread_name { thread_name } else { b"<unnamed>" }
@@ -202,28 +196,21 @@ impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> {
202
196
}
203
197
}
204
198
205
- impl < ' mir , ' tcx > Default for Thread < ' mir , ' tcx > {
206
- fn default ( ) -> Self {
199
+ impl < ' mir , ' tcx > Thread < ' mir , ' tcx > {
200
+ fn new ( name : Option < & str > , on_stack_empty : Option < StackEmptyCallback < ' mir , ' tcx > > ) -> Self {
207
201
Self {
208
202
state : ThreadState :: Enabled ,
209
- thread_name : None ,
203
+ thread_name : name . map ( |name| Vec :: from ( name . as_bytes ( ) ) ) ,
210
204
stack : Vec :: new ( ) ,
211
205
top_user_relevant_frame : None ,
212
206
join_status : ThreadJoinStatus :: Joinable ,
213
207
panic_payload : None ,
214
208
last_error : None ,
209
+ on_stack_empty,
215
210
}
216
211
}
217
212
}
218
213
219
- impl < ' mir , ' tcx > Thread < ' mir , ' tcx > {
220
- fn new ( name : & str ) -> Self {
221
- let mut thread = Thread :: default ( ) ;
222
- thread. thread_name = Some ( Vec :: from ( name. as_bytes ( ) ) ) ;
223
- thread
224
- }
225
- }
226
-
227
214
impl VisitTags for Thread < ' _ , ' _ > {
228
215
fn visit_tags ( & self , visit : & mut dyn FnMut ( SbTag ) ) {
229
216
let Thread {
@@ -234,6 +221,7 @@ impl VisitTags for Thread<'_, '_> {
234
221
state : _,
235
222
thread_name : _,
236
223
join_status : _,
224
+ on_stack_empty : _, // we assume the closure captures no GC-relevant state
237
225
} = self ;
238
226
239
227
panic_payload. visit_tags ( visit) ;
@@ -327,22 +315,6 @@ pub struct ThreadManager<'mir, 'tcx> {
327
315
timeout_callbacks : FxHashMap < ThreadId , TimeoutCallbackInfo < ' mir , ' tcx > > ,
328
316
}
329
317
330
- impl < ' mir , ' tcx > Default for ThreadManager < ' mir , ' tcx > {
331
- fn default ( ) -> Self {
332
- let mut threads = IndexVec :: new ( ) ;
333
- // Create the main thread and add it to the list of threads.
334
- threads. push ( Thread :: new ( "main" ) ) ;
335
- Self {
336
- active_thread : ThreadId :: new ( 0 ) ,
337
- threads,
338
- sync : SynchronizationState :: default ( ) ,
339
- thread_local_alloc_ids : Default :: default ( ) ,
340
- yield_active_thread : false ,
341
- timeout_callbacks : FxHashMap :: default ( ) ,
342
- }
343
- }
344
- }
345
-
346
318
impl VisitTags for ThreadManager < ' _ , ' _ > {
347
319
fn visit_tags ( & self , visit : & mut dyn FnMut ( SbTag ) ) {
348
320
let ThreadManager {
@@ -367,8 +339,28 @@ impl VisitTags for ThreadManager<'_, '_> {
367
339
}
368
340
}
369
341
342
+ impl < ' mir , ' tcx > Default for ThreadManager < ' mir , ' tcx > {
343
+ fn default ( ) -> Self {
344
+ let mut threads = IndexVec :: new ( ) ;
345
+ // Create the main thread and add it to the list of threads.
346
+ threads. push ( Thread :: new ( Some ( "main" ) , None ) ) ;
347
+ Self {
348
+ active_thread : ThreadId :: new ( 0 ) ,
349
+ threads,
350
+ sync : SynchronizationState :: default ( ) ,
351
+ thread_local_alloc_ids : Default :: default ( ) ,
352
+ yield_active_thread : false ,
353
+ timeout_callbacks : FxHashMap :: default ( ) ,
354
+ }
355
+ }
356
+ }
357
+
370
358
impl < ' mir , ' tcx : ' mir > ThreadManager < ' mir , ' tcx > {
371
- pub ( crate ) fn init ( ecx : & mut MiriInterpCx < ' mir , ' tcx > ) {
359
+ pub ( crate ) fn init (
360
+ ecx : & mut MiriInterpCx < ' mir , ' tcx > ,
361
+ on_main_stack_empty : StackEmptyCallback < ' mir , ' tcx > ,
362
+ ) {
363
+ ecx. machine . threads . threads [ ThreadId :: new ( 0 ) ] . on_stack_empty = Some ( on_main_stack_empty) ;
372
364
if ecx. tcx . sess . target . os . as_ref ( ) != "windows" {
373
365
// The main thread can *not* be joined on except on windows.
374
366
ecx. machine . threads . threads [ ThreadId :: new ( 0 ) ] . join_status = ThreadJoinStatus :: Detached ;
@@ -411,9 +403,9 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
411
403
}
412
404
413
405
/// Create a new thread and returns its id.
414
- fn create_thread ( & mut self ) -> ThreadId {
406
+ fn create_thread ( & mut self , on_stack_empty : StackEmptyCallback < ' mir , ' tcx > ) -> ThreadId {
415
407
let new_thread_id = ThreadId :: new ( self . threads . len ( ) ) ;
416
- self . threads . push ( Default :: default ( ) ) ;
408
+ self . threads . push ( Thread :: new ( None , Some ( on_stack_empty ) ) ) ;
417
409
new_thread_id
418
410
}
419
411
@@ -458,6 +450,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
458
450
}
459
451
460
452
/// Get a mutable borrow of the currently active thread.
453
+ /// (Private for a bit of protection.)
461
454
fn active_thread_mut ( & mut self ) -> & mut Thread < ' mir , ' tcx > {
462
455
& mut self . threads [ self . active_thread ]
463
456
}
@@ -669,37 +662,25 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
669
662
/// long as we can and switch only when we have to (the active thread was
670
663
/// blocked, terminated, or has explicitly asked to be preempted).
671
664
fn schedule ( & mut self , clock : & Clock ) -> InterpResult < ' tcx , SchedulingAction > {
672
- // Check whether the thread has **just** terminated (`check_terminated`
673
- // checks whether the thread has popped all its stack and if yes, sets
674
- // the thread state to terminated).
675
- if self . threads [ self . active_thread ] . check_terminated ( ) {
676
- return Ok ( SchedulingAction :: ExecuteDtors ) ;
677
- }
678
- // If we get here again and the thread is *still* terminated, there are no more dtors to run.
679
- if self . threads [ MAIN_THREAD ] . state == ThreadState :: Terminated {
680
- // The main thread terminated; stop the program.
681
- // We do *not* run TLS dtors of remaining threads, which seems to match rustc behavior.
682
- return Ok ( SchedulingAction :: Stop ) ;
683
- }
684
665
// This thread and the program can keep going.
685
666
if self . threads [ self . active_thread ] . state == ThreadState :: Enabled
686
667
&& !self . yield_active_thread
687
668
{
688
669
// The currently active thread is still enabled, just continue with it.
689
670
return Ok ( SchedulingAction :: ExecuteStep ) ;
690
671
}
691
- // The active thread yielded. Let's see if there are any timeouts to take care of. We do
692
- // this *before* running any other thread, to ensure that timeouts "in the past" fire before
693
- // any other thread can take an action. This ensures that for `pthread_cond_timedwait`, "an
694
- // error is returned if [...] the absolute time specified by abstime has already been passed
695
- // at the time of the call".
672
+ // The active thread yielded or got terminated . Let's see if there are any timeouts to take
673
+ // care of. We do this *before* running any other thread, to ensure that timeouts "in the
674
+ // past" fire before any other thread can take an action. This ensures that for
675
+ // `pthread_cond_timedwait`, "an error is returned if [...] the absolute time specified by
676
+ // abstime has already been passed at the time of the call".
696
677
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
697
678
let potential_sleep_time =
698
679
self . timeout_callbacks . values ( ) . map ( |info| info. call_time . get_wait_time ( clock) ) . min ( ) ;
699
680
if potential_sleep_time == Some ( Duration :: new ( 0 , 0 ) ) {
700
681
return Ok ( SchedulingAction :: ExecuteTimeoutCallback ) ;
701
682
}
702
- // No callbacks scheduled, pick a regular thread to execute.
683
+ // No callbacks immediately scheduled, pick a regular thread to execute.
703
684
// The active thread blocked or yielded. So we go search for another enabled thread.
704
685
// Crucially, we start searching at the current active thread ID, rather than at 0, since we
705
686
// want to avoid always scheduling threads 0 and 1 without ever making progress in thread 2.
@@ -730,9 +711,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
730
711
// All threads are currently blocked, but we have unexecuted
731
712
// timeout_callbacks, which may unblock some of the threads. Hence,
732
713
// sleep until the first callback.
733
-
734
- clock. sleep ( sleep_time) ;
735
- Ok ( SchedulingAction :: ExecuteTimeoutCallback )
714
+ Ok ( SchedulingAction :: Sleep ( sleep_time) )
736
715
} else {
737
716
throw_machine_stop ! ( TerminationInfo :: Deadlock ) ;
738
717
}
@@ -773,18 +752,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
773
752
}
774
753
}
775
754
755
+ /// Start a regular (non-main) thread.
776
756
#[ inline]
777
- fn create_thread ( & mut self ) -> ThreadId {
778
- let this = self . eval_context_mut ( ) ;
779
- let id = this. machine . threads . create_thread ( ) ;
780
- if let Some ( data_race) = & mut this. machine . data_race {
781
- data_race. thread_created ( & this. machine . threads , id) ;
782
- }
783
- id
784
- }
785
-
786
- #[ inline]
787
- fn start_thread (
757
+ fn start_regular_thread (
788
758
& mut self ,
789
759
thread : Option < MPlaceTy < ' tcx , Provenance > > ,
790
760
start_routine : Pointer < Option < Provenance > > ,
@@ -795,7 +765,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
795
765
let this = self . eval_context_mut ( ) ;
796
766
797
767
// Create the new thread
798
- let new_thread_id = this. create_thread ( ) ;
768
+ let new_thread_id = this. machine . threads . create_thread ( {
769
+ let mut state = tls:: TlsDtorsState :: default ( ) ;
770
+ Box :: new ( move |m| state. on_stack_empty ( m) )
771
+ } ) ;
772
+ if let Some ( data_race) = & mut this. machine . data_race {
773
+ data_race. thread_created ( & this. machine . threads , new_thread_id) ;
774
+ }
799
775
800
776
// Write the current thread-id, switch to the next thread later
801
777
// to treat this write operation as occuring on the current thread.
@@ -888,12 +864,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
888
864
this. machine . threads . get_total_thread_count ( )
889
865
}
890
866
891
- #[ inline]
892
- fn has_terminated ( & self , thread_id : ThreadId ) -> bool {
893
- let this = self . eval_context_ref ( ) ;
894
- this. machine . threads . has_terminated ( thread_id)
895
- }
896
-
897
867
#[ inline]
898
868
fn have_all_terminated ( & self ) -> bool {
899
869
let this = self . eval_context_ref ( ) ;
@@ -943,26 +913,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
943
913
where
944
914
' mir : ' c ,
945
915
{
946
- let this = self . eval_context_ref ( ) ;
947
- this. machine . threads . get_thread_name ( thread)
916
+ self . eval_context_ref ( ) . machine . threads . get_thread_name ( thread)
948
917
}
949
918
950
919
#[ inline]
951
920
fn block_thread ( & mut self , thread : ThreadId ) {
952
- let this = self . eval_context_mut ( ) ;
953
- this. machine . threads . block_thread ( thread) ;
921
+ self . eval_context_mut ( ) . machine . threads . block_thread ( thread) ;
954
922
}
955
923
956
924
#[ inline]
957
925
fn unblock_thread ( & mut self , thread : ThreadId ) {
958
- let this = self . eval_context_mut ( ) ;
959
- this. machine . threads . unblock_thread ( thread) ;
926
+ self . eval_context_mut ( ) . machine . threads . unblock_thread ( thread) ;
960
927
}
961
928
962
929
#[ inline]
963
930
fn yield_active_thread ( & mut self ) {
964
- let this = self . eval_context_mut ( ) ;
965
- this. machine . threads . yield_active_thread ( ) ;
931
+ self . eval_context_mut ( ) . machine . threads . yield_active_thread ( ) ;
966
932
}
967
933
968
934
#[ inline]
@@ -1024,6 +990,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
1024
990
Ok ( ( ) )
1025
991
}
1026
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
+
1027
1006
/// Decide which action to take next and on which thread.
1028
1007
#[ inline]
1029
1008
fn schedule ( & mut self ) -> InterpResult < ' tcx , SchedulingAction > {
@@ -1034,10 +1013,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
1034
1013
/// Handles thread termination of the active thread: wakes up threads joining on this one,
1035
1014
/// and deallocated thread-local statics.
1036
1015
///
1037
- /// This is called from `tls.rs` after handling the TLS dtors .
1016
+ /// This is called by the eval loop when a thread's on_stack_empty returns `Ready` .
1038
1017
#[ inline]
1039
1018
fn thread_terminated ( & mut self ) -> InterpResult < ' tcx > {
1040
1019
let this = self . eval_context_mut ( ) ;
1020
+ let thread = this. active_thread_mut ( ) ;
1021
+ assert ! ( thread. stack. is_empty( ) , "only threads with an empty stack can be terminated" ) ;
1022
+ thread. state = ThreadState :: Terminated ;
1023
+
1041
1024
for ptr in this. machine . threads . thread_terminated ( this. machine . data_race . as_mut ( ) ) {
1042
1025
this. deallocate_ptr ( ptr. into ( ) , None , MiriMemoryKind :: Tls . into ( ) ) ?;
1043
1026
}
0 commit comments