Clarify comments about TLS destructor handling in Windows, add a test for TLS destructors.

This commit is contained in:
Vytautas Astrauskas 2020-04-19 20:52:53 -07:00
parent e4dc3567f8
commit 9a01c3fa3e
7 changed files with 67 additions and 3 deletions

View File

@ -222,7 +222,8 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
// Read the return code pointer *before* we run TLS destructors, to assert
// that it was written to by the time that `start` lang item returned.
let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
// Global destructors.
// Run Windows destructors. (We do not support concurrency on Windows
// yet, so we run the destructor of the main thread separately.)
ecx.run_windows_tls_dtors()?;
Ok(return_code)
})();

View File

@ -6,7 +6,6 @@ use std::collections::HashSet;
use log::trace;
use rustc_index::vec::Idx;
use rustc_middle::ty;
use rustc_target::abi::{Size, HasDataLayout};
@ -201,7 +200,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
return Ok(());
}
let active_thread = this.get_active_thread()?;
assert_eq!(active_thread.index(), 0, "concurrency on Windows not supported");
assert_eq!(this.get_total_thread_count()?, 1, "concurrency on Windows not supported");
assert!(!this.machine.tls.dtors_running.contains(&active_thread), "running TLS dtors twice");
this.machine.tls.dtors_running.insert(active_thread);
// Windows has a special magic linker section that is run on certain events.

View File

@ -227,6 +227,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
self.active_thread
}
/// Get the total number of threads that were ever spawn by this program.
fn get_total_thread_count(&self) -> usize {
self.threads.len()
}
/// Has the given thread terminated?
fn has_terminated(&self, thread_id: ThreadId) -> bool {
self.threads[thread_id].state == ThreadState::Terminated
@ -492,6 +497,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
Ok(this.machine.threads.get_active_thread_id())
}
#[inline]
fn get_total_thread_count(&self) -> InterpResult<'tcx, usize> {
let this = self.eval_context_ref();
Ok(this.machine.threads.get_total_thread_count())
}
#[inline]
fn has_terminated(&self, thread_id: ThreadId) -> InterpResult<'tcx, bool> {
let this = self.eval_context_ref();

View File

@ -1,5 +1,8 @@
// ignore-windows: Concurrency on Windows is not supported yet.
//! Check that we catch if a thread local is accessed after the thread has
//! terminated.
#![feature(thread_local_internals)]
use std::cell::RefCell;

View File

@ -0,0 +1,46 @@
// ignore-windows: Concurrency on Windows is not supported yet.
//! Check that destructors of the library thread locals are executed immediately
//! after a thread terminates.
#![feature(thread_local_internals)]
use std::cell::RefCell;
use std::thread;
struct TestCell {
value: RefCell<u8>,
}
impl Drop for TestCell {
fn drop(&mut self) {
println!("Dropping: {}", self.value.borrow())
}
}
static A: std::thread::LocalKey<TestCell> = {
#[inline]
fn __init() -> TestCell {
TestCell { value: RefCell::new(0) }
}
unsafe fn __getit() -> Option<&'static TestCell> {
static __KEY: std::thread::__OsLocalKeyInner<TestCell> =
std::thread::__OsLocalKeyInner::new();
__KEY.get(__init)
}
unsafe { std::thread::LocalKey::new(__getit) }
};
fn main() {
thread::spawn(|| {
A.with(|f| {
assert_eq!(*f.value.borrow(), 0);
*f.value.borrow_mut() = 5;
});
})
.join()
.unwrap();
println!("Continue main.")
}

View File

@ -0,0 +1,2 @@
warning: thread support is experimental. For example, Miri does not detect data races yet.

View File

@ -0,0 +1,2 @@
Dropping: 5
Continue main.