@@ -36,9 +36,9 @@ pub struct TlsData<'tcx> {
36
36
/// pthreads-style thread-local storage.
37
37
keys : BTreeMap < TlsKey , TlsEntry < ' tcx > > ,
38
38
39
- /// A single per thread destructor of the thread local storage (that's how
40
- /// things work on macOS) with a data argument .
41
- macos_thread_dtors : BTreeMap < ThreadId , ( ty:: Instance < ' tcx > , Scalar ) > ,
39
+ /// On macOS, each thread holds a list of destructor functions with their
40
+ /// respective data arguments .
41
+ macos_thread_dtors : BTreeMap < ThreadId , Vec < ( ty:: Instance < ' tcx > , Scalar ) > > ,
42
42
}
43
43
44
44
impl < ' tcx > Default for TlsData < ' tcx > {
@@ -119,26 +119,15 @@ impl<'tcx> TlsData<'tcx> {
119
119
}
120
120
}
121
121
122
- /// Set the thread wide destructor of the thread local storage for the given
123
- /// thread. This function is used to implement `_tlv_atexit` shim on MacOS.
124
- ///
125
- /// Thread wide dtors are available only on MacOS. There is one destructor
126
- /// per thread as can be guessed from the following comment in the
127
- /// [`_tlv_atexit`
128
- /// implementation](https://github.com/opensource-apple/dyld/blob/195030646877261f0c8c7ad8b001f52d6a26f514/src/threadLocalVariables.c#L389):
129
- ///
130
- /// NOTE: this does not need locks because it only operates on current thread data
131
- pub fn set_macos_thread_dtor (
122
+ /// Add a thread local storage destructor for the given thread. This function
123
+ /// is used to implement the `_tlv_atexit` shim on MacOS.
124
+ pub fn add_macos_thread_dtor (
132
125
& mut self ,
133
126
thread : ThreadId ,
134
127
dtor : ty:: Instance < ' tcx > ,
135
128
data : Scalar ,
136
129
) -> InterpResult < ' tcx > {
137
- if self . macos_thread_dtors . insert ( thread, ( dtor, data) ) . is_some ( ) {
138
- throw_unsup_format ! (
139
- "setting more than one thread local storage destructor for the same thread is not supported"
140
- ) ;
141
- }
130
+ self . macos_thread_dtors . entry ( thread) . or_default ( ) . push ( ( dtor, data) ) ;
142
131
Ok ( ( ) )
143
132
}
144
133
@@ -202,6 +191,10 @@ impl<'tcx> TlsData<'tcx> {
202
191
for TlsEntry { data, .. } in self . keys . values_mut ( ) {
203
192
data. remove ( & thread_id) ;
204
193
}
194
+
195
+ if let Some ( dtors) = self . macos_thread_dtors . remove ( & thread_id) {
196
+ assert ! ( dtors. is_empty( ) , "the destructors should have already been run" ) ;
197
+ }
205
198
}
206
199
}
207
200
@@ -212,7 +205,7 @@ impl VisitProvenance for TlsData<'_> {
212
205
for scalar in keys. values ( ) . flat_map ( |v| v. data . values ( ) ) {
213
206
scalar. visit_provenance ( visit) ;
214
207
}
215
- for ( _, scalar) in macos_thread_dtors. values ( ) {
208
+ for ( _, scalar) in macos_thread_dtors. values ( ) . flatten ( ) {
216
209
scalar. visit_provenance ( visit) ;
217
210
}
218
211
}
@@ -225,6 +218,7 @@ pub struct TlsDtorsState<'tcx>(TlsDtorsStatePriv<'tcx>);
225
218
enum TlsDtorsStatePriv < ' tcx > {
226
219
#[ default]
227
220
Init ,
221
+ MacOsDtors ,
228
222
PthreadDtors ( RunningDtorState ) ,
229
223
/// For Windows Dtors, we store the list of functions that we still have to call.
230
224
/// These are functions from the magic `.CRT$XLB` linker section.
@@ -243,11 +237,10 @@ impl<'tcx> TlsDtorsState<'tcx> {
243
237
Init => {
244
238
match this. tcx . sess . target . os . as_ref ( ) {
245
239
"macos" => {
246
- // The macOS thread wide destructor runs "before any TLS slots get
247
- // freed", so do that first.
248
- this. schedule_macos_tls_dtor ( ) ?;
249
- // When that destructor is done, go on with the pthread dtors.
250
- break ' new_state PthreadDtors ( Default :: default ( ) ) ;
240
+ // macOS has a _tlv_atexit function that allows
241
+ // registering destructors without associated keys.
242
+ // These are run first.
243
+ break ' new_state MacOsDtors ;
251
244
}
252
245
_ if this. target_os_is_unix ( ) => {
253
246
// All other Unixes directly jump to running the pthread dtors.
@@ -266,6 +259,14 @@ impl<'tcx> TlsDtorsState<'tcx> {
266
259
}
267
260
}
268
261
}
262
+ MacOsDtors => {
263
+ match this. schedule_macos_tls_dtor ( ) ? {
264
+ Poll :: Pending => return Ok ( Poll :: Pending ) ,
265
+ // After all macOS destructors are run, the system switches
266
+ // to destroying the pthread destructors.
267
+ Poll :: Ready ( ( ) ) => break ' new_state PthreadDtors ( Default :: default ( ) ) ,
268
+ }
269
+ }
269
270
PthreadDtors ( state) => {
270
271
match this. schedule_next_pthread_tls_dtor ( state) ? {
271
272
Poll :: Pending => return Ok ( Poll :: Pending ) , // just keep going
@@ -328,12 +329,15 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
328
329
Ok ( ( ) )
329
330
}
330
331
331
- /// Schedule the MacOS thread destructor of the thread local storage to be
332
- /// executed.
333
- fn schedule_macos_tls_dtor ( & mut self ) -> InterpResult < ' tcx > {
332
+ /// Schedule the macOS thread local storage destructors to be executed.
333
+ fn schedule_macos_tls_dtor ( & mut self ) -> InterpResult < ' tcx , Poll < ( ) > > {
334
334
let this = self . eval_context_mut ( ) ;
335
335
let thread_id = this. active_thread ( ) ;
336
- if let Some ( ( instance, data) ) = this. machine . tls . macos_thread_dtors . remove ( & thread_id) {
336
+ // macOS keeps track of TLS destructors in a stack. If a destructor
337
+ // registers another destructor, it will be run next.
338
+ // See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277
339
+ let dtor = this. machine . tls . macos_thread_dtors . get_mut ( & thread_id) . and_then ( Vec :: pop) ;
340
+ if let Some ( ( instance, data) ) = dtor {
337
341
trace ! ( "Running macos dtor {:?} on {:?} at {:?}" , instance, data, thread_id) ;
338
342
339
343
this. call_function (
@@ -343,8 +347,11 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
343
347
None ,
344
348
StackPopCleanup :: Root { cleanup : true } ,
345
349
) ?;
350
+
351
+ return Ok ( Poll :: Pending ) ;
346
352
}
347
- Ok ( ( ) )
353
+
354
+ Ok ( Poll :: Ready ( ( ) ) )
348
355
}
349
356
350
357
/// Schedule a pthread TLS destructor. Returns `true` if found
0 commit comments