@@ -38,6 +38,9 @@ pub struct TlsData<'tcx> {
38
38
39
39
/// Whether we are in the "destruct" phase, during which some operations are UB.
40
40
dtors_running : HashSet < ThreadId > ,
41
+
42
+ /// The last TlsKey used to retrieve a TLS destructor.
43
+ last_dtor_key : BTreeMap < ThreadId , TlsKey > ,
41
44
}
42
45
43
46
impl < ' tcx > Default for TlsData < ' tcx > {
@@ -47,6 +50,7 @@ impl<'tcx> Default for TlsData<'tcx> {
47
50
keys : Default :: default ( ) ,
48
51
global_dtors : Default :: default ( ) ,
49
52
dtors_running : Default :: default ( ) ,
53
+ last_dtor_key : Default :: default ( ) ,
50
54
}
51
55
}
52
56
}
@@ -187,21 +191,15 @@ impl<'tcx> TlsData<'tcx> {
187
191
}
188
192
}
189
193
190
- impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
191
- pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
192
-
193
- /// Run TLS destructors for the main thread on Windows. The implementation
194
- /// assumes that we do not support concurrency on Windows yet.
195
- ///
196
- /// Note: on non-Windows OS this function is a no-op.
197
- fn run_windows_tls_dtors ( & mut self ) -> InterpResult < ' tcx > {
194
+ impl < ' mir , ' tcx : ' mir > EvalContextPrivExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
195
+ trait EvalContextPrivExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
196
+ /// Schedule TLS destructors for the main thread on Windows. The
197
+ /// implementation assumes that we do not support concurrency on Windows
198
+ /// yet.
199
+ fn schedule_windows_tls_dtors ( & mut self ) -> InterpResult < ' tcx > {
198
200
let this = self . eval_context_mut ( ) ;
199
- if this. tcx . sess . target . target . target_os != "windows" {
200
- return Ok ( ( ) ) ;
201
- }
202
201
let active_thread = this. get_active_thread ( ) ?;
203
202
assert_eq ! ( this. get_total_thread_count( ) ?, 1 , "concurrency on Windows not supported" ) ;
204
- assert ! ( !this. machine. tls. dtors_running. contains( & active_thread) , "running TLS dtors twice" ) ;
205
203
this. machine . tls . dtors_running . insert ( active_thread) ;
206
204
// Windows has a special magic linker section that is run on certain events.
207
205
// Instead of searching for that section and supporting arbitrary hooks in there
@@ -221,30 +219,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
221
219
StackPopCleanup :: None { cleanup : true } ,
222
220
) ?;
223
221
224
- // step until out of stackframes
225
- this. run ( ) ?;
226
-
227
- // Windows doesn't have other destructors.
222
+ this. enable_thread ( active_thread) ?;
228
223
Ok ( ( ) )
229
224
}
230
225
231
- /// Run TLS destructors for the active thread .
226
+ /// Schedule the MacOS global dtor to be executed .
232
227
///
233
- /// Note: on Windows OS this function is a no-op because we do not support
234
- /// concurrency on Windows yet.
235
- ///
236
- /// FIXME: we do not support yet deallocation of thread local statics.
237
- fn run_tls_dtors_for_active_thread ( & mut self ) -> InterpResult < ' tcx > {
228
+ /// Note: It is safe to call this function also on other Unixes.
229
+ fn schedule_macos_global_tls_dtors ( & mut self ) -> InterpResult < ' tcx > {
238
230
let this = self . eval_context_mut ( ) ;
239
- if this. tcx . sess . target . target . target_os == "windows" {
240
- return Ok ( ( ) ) ;
241
- }
242
231
let thread_id = this. get_active_thread ( ) ?;
243
- assert ! ( !this. machine. tls. dtors_running. contains( & thread_id) , "running TLS dtors twice" ) ;
244
- this. machine . tls . dtors_running . insert ( thread_id) ;
245
-
246
232
// The macOS global dtor runs "before any TLS slots get freed", so do that first.
247
- if let Some ( & ( instance, data) ) = this. machine . tls . global_dtors . get ( & thread_id) {
233
+ if let Some ( ( instance, data) ) = this. machine . tls . global_dtors . remove ( & thread_id) {
248
234
trace ! ( "Running global dtor {:?} on {:?} at {:?}" , instance, data, thread_id) ;
249
235
250
236
let ret_place = MPlaceTy :: dangling ( this. machine . layouts . unit , this) . into ( ) ;
@@ -255,14 +241,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
255
241
StackPopCleanup :: None { cleanup : true } ,
256
242
) ?;
257
243
258
- // step until out of stackframes
259
- this. run ( ) ?;
244
+ // Enable the thread so that it steps through the destructor which
245
+ // we just scheduled. Since we deleted the destructor, it is
246
+ // guaranteed that we will schedule it again. The `dtors_running`
247
+ // flag will prevent the code from adding the destructor again.
248
+ this. enable_thread ( thread_id) ?;
260
249
}
250
+ Ok ( ( ) )
251
+ }
252
+
253
+ /// Schedule a pthread TLS destructor.
254
+ fn schedule_pthread_tls_dtors ( & mut self ) -> InterpResult < ' tcx > {
255
+ let this = self . eval_context_mut ( ) ;
256
+ let active_thread = this. get_active_thread ( ) ?;
261
257
262
- assert ! ( this. has_terminated( thread_id) ?, "running TLS dtors for non-terminated thread" ) ;
263
- let mut dtor = this. machine . tls . fetch_tls_dtor ( None , thread_id) ;
264
- while let Some ( ( instance, ptr, key) ) = dtor {
265
- trace ! ( "Running TLS dtor {:?} on {:?} at {:?}" , instance, ptr, thread_id) ;
258
+ assert ! ( this. has_terminated( active_thread) ?, "running TLS dtors for non-terminated thread" ) ;
259
+ // Fetch next dtor after `key`.
260
+ let last_key = this. machine . tls . last_dtor_key . get ( & active_thread) . cloned ( ) ;
261
+ let dtor = match this. machine . tls . fetch_tls_dtor ( last_key, active_thread) {
262
+ dtor @ Some ( _) => dtor,
263
+ // We ran each dtor once, start over from the beginning.
264
+ None => {
265
+ this. machine . tls . fetch_tls_dtor ( None , active_thread)
266
+ }
267
+ } ;
268
+ if let Some ( ( instance, ptr, key) ) = dtor {
269
+ this. machine . tls . last_dtor_key . insert ( active_thread, key) ;
270
+ trace ! ( "Running TLS dtor {:?} on {:?} at {:?}" , instance, ptr, active_thread) ;
266
271
assert ! ( !this. is_null( ptr) . unwrap( ) , "Data can't be NULL when dtor is called!" ) ;
267
272
268
273
let ret_place = MPlaceTy :: dangling ( this. machine . layouts . unit , this) . into ( ) ;
@@ -273,15 +278,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
273
278
StackPopCleanup :: None { cleanup : true } ,
274
279
) ?;
275
280
276
- // step until out of stackframes
277
- this. run ( ) ?;
281
+ this. enable_thread ( active_thread) ?;
282
+ return Ok ( ( ) ) ;
283
+ }
284
+ this. machine . tls . last_dtor_key . remove ( & active_thread) ;
285
+
286
+ Ok ( ( ) )
287
+ }
288
+ }
289
+
290
+ impl < ' mir , ' tcx : ' mir > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
291
+ pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
278
292
279
- // Fetch next dtor after `key`.
280
- dtor = match this. machine . tls . fetch_tls_dtor ( Some ( key) , thread_id) {
281
- dtor @ Some ( _) => dtor,
282
- // We ran each dtor once, start over from the beginning.
283
- None => this. machine . tls . fetch_tls_dtor ( None , thread_id) ,
284
- } ;
293
+ /// Schedule an active thread's TLS destructor to run on the active thread.
294
+ /// Note that this function does not run the destructors itself, it just
295
+ /// schedules them one by one each time it is called.
296
+ ///
297
+ /// FIXME: we do not support yet deallocation of thread local statics.
298
+ fn schedule_tls_dtors_for_active_thread ( & mut self ) -> InterpResult < ' tcx > {
299
+ let this = self . eval_context_mut ( ) ;
300
+ let active_thread = this. get_active_thread ( ) ?;
301
+
302
+ if this. tcx . sess . target . target . target_os == "windows" {
303
+ if !this. machine . tls . dtors_running . contains ( & active_thread) {
304
+ this. machine . tls . dtors_running . insert ( active_thread) ;
305
+ this. schedule_windows_tls_dtors ( ) ?;
306
+ }
307
+ } else {
308
+ this. machine . tls . dtors_running . insert ( active_thread) ;
309
+ this. schedule_macos_global_tls_dtors ( ) ?;
310
+ this. schedule_pthread_tls_dtors ( ) ?;
285
311
}
286
312
287
313
Ok ( ( ) )
0 commit comments