@@ -33,6 +33,15 @@ enum SchedulingAction {
33
33
Sleep ( Duration ) ,
34
34
}
35
35
36
+ /// What to do with TLS allocations from terminated threads
37
+ pub enum TlsAllocAction {
38
+ /// Deallocate backing memory of thread-local statics as usual
39
+ Deallocate ,
40
+ /// Skip deallocating backing memory of thread-local statics and consider all memory reachable
41
+ /// from them as "allowed to leak" (like global `static`s).
42
+ Leak ,
43
+ }
44
+
36
45
/// Trait for callbacks that can be executed when some event happens, such as after a timeout.
37
46
pub trait MachineCallback < ' mir , ' tcx > : VisitTags {
38
47
fn call ( & self , ecx : & mut InterpCx < ' mir , ' tcx , MiriMachine < ' mir , ' tcx > > ) -> InterpResult < ' tcx > ;
@@ -1051,7 +1060,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
1051
1060
// See if this thread can do something else.
1052
1061
match this. run_on_stack_empty ( ) ? {
1053
1062
Poll :: Pending => { } // keep going
1054
- Poll :: Ready ( ( ) ) => this. terminate_active_thread ( ) ?,
1063
+ Poll :: Ready ( ( ) ) =>
1064
+ this. terminate_active_thread ( TlsAllocAction :: Deallocate ) ?,
1055
1065
}
1056
1066
}
1057
1067
}
@@ -1066,21 +1076,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
1066
1076
}
1067
1077
1068
1078
/// Handles thread termination of the active thread: wakes up threads joining on this one,
1069
- /// and deallocated thread-local statics.
1079
+ /// and deals with the thread's thread -local statics according to `tls_alloc_action` .
1070
1080
///
1071
1081
/// This is called by the eval loop when a thread's on_stack_empty returns `Ready`.
1072
1082
#[ inline]
1073
- fn terminate_active_thread ( & mut self ) -> InterpResult < ' tcx > {
1083
+ fn terminate_active_thread ( & mut self , tls_alloc_action : TlsAllocAction ) -> InterpResult < ' tcx > {
1074
1084
let this = self . eval_context_mut ( ) ;
1075
1085
let thread = this. active_thread_mut ( ) ;
1076
1086
assert ! ( thread. stack. is_empty( ) , "only threads with an empty stack can be terminated" ) ;
1077
1087
thread. state = ThreadState :: Terminated ;
1078
1088
1079
1089
let current_span = this. machine . current_span ( ) ;
1080
- for ptr in
1081
- this. machine . threads . thread_terminated ( this. machine . data_race . as_mut ( ) , current_span)
1082
- {
1083
- this. deallocate_ptr ( ptr. into ( ) , None , MiriMemoryKind :: Tls . into ( ) ) ?;
1090
+ let thread_local_allocations =
1091
+ this. machine . threads . thread_terminated ( this. machine . data_race . as_mut ( ) , current_span) ;
1092
+ for ptr in thread_local_allocations {
1093
+ match tls_alloc_action {
1094
+ TlsAllocAction :: Deallocate =>
1095
+ this. deallocate_ptr ( ptr. into ( ) , None , MiriMemoryKind :: Tls . into ( ) ) ?,
1096
+ TlsAllocAction :: Leak =>
1097
+ if let Some ( alloc) = ptr. provenance . get_alloc_id ( ) {
1098
+ trace ! ( "Thread-local static leaked and stored as static root: {:?}" , alloc) ;
1099
+ this. machine . static_roots . push ( alloc) ;
1100
+ } ,
1101
+ }
1084
1102
}
1085
1103
Ok ( ( ) )
1086
1104
}
0 commit comments