- // Fetch next dtor after `key`.
- dtor = match this.machine.tls.fetch_tls_dtor(Some(key), thread_id) {
- dtor @ Some(_) => dtor,
- // We ran each dtor once, start over from the beginning.
- None => this.machine.tls.fetch_tls_dtor(None, thread_id),
- };
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
+
+ /// Schedule an active thread's TLS destructor to run on the active thread.
+ /// Note that this function does not run the destructors itself, it just
+ /// schedules them one by one each time it is called and reenables the
+ /// thread so that it can be executed normally by the main execution loop.
+ ///
+ /// Note: we consistently run TLS destructors for all threads, including the
+ /// main thread. However, it is not clear that we should run the TLS
+ /// destructors for the main thread. See issue:
+ /// https://github.com/rust-lang/rust/issues/28129.
+ fn schedule_next_tls_dtor_for_active_thread(&mut self) -> InterpResult<'tcx> {
+ let this = self.eval_context_mut();
+ let active_thread = this.get_active_thread();
+
+ if !this.machine.tls.set_dtors_running_for_thread(active_thread) {
+ // This is the first time we got asked to schedule a destructor. The
+ // Windows schedule destructor function must be called exactly once,
+ // this is why it is in this block.
+ if this.tcx.sess.target.os == "windows" {
+ // On Windows, we signal that the thread quit by starting the
+ // relevant function, reenabling the thread, and going back to
+ // the scheduler.
+ this.schedule_windows_tls_dtors()?;
+ return Ok(())
+ }
+ }
+ // The remaining dtors make some progress each time around the scheduler loop,
+ // until they return `false` to indicate that they are done.
+
+ // The macOS thread wide destructor runs "before any TLS slots get
+ // freed", so do that first.
+ if this.schedule_macos_tls_dtor()? {
+ // We have scheduled a MacOS dtor to run on the thread. Execute it
+ // to completion and come back here. Scheduling a destructor
+ // destroys it, so we will not enter this branch again.
+ return Ok(())