also ignore 'thread leaks' with -Zmiri-ignore-leaks

This commit is contained in:
Ralf Jung 2021-07-25 14:08:12 +02:00
parent e2872a3f2a
commit 71efd950d1
6 changed files with 49 additions and 12 deletions

View File

@ -230,7 +230,8 @@ environment variable:
the host so that it cannot be accessed by the program. Can be used multiple
times to exclude several variables. On Windows, the `TERM` environment
variable is excluded by default.
* `-Zmiri-ignore-leaks` disables the memory leak checker.
* `-Zmiri-ignore-leaks` disables the memory leak checker, and also allows some
remaining threads to exist when the main thread exits.
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
This can be used to find which parts of your program are executing slowly under Miri.
The profile is written out to a file with the prefix `<name>`, and can be processed

View File

@ -300,6 +300,12 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
match res {
Ok(return_code) => {
if !ignore_leaks {
// Check for thread leaks.
if !ecx.have_all_terminated() {
tcx.sess.err("the main thread terminated without waiting for all remaining threads");
return None;
}
// Check for memory leaks.
info!("Additonal static roots: {:?}", ecx.machine.static_roots);
let leaks = ecx.memory.leak_report(&ecx.machine.static_roots);
if leaks != 0 {

View File

@ -302,6 +302,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
self.threads[thread_id].state == ThreadState::Terminated
}
/// Have all threads terminated?
fn have_all_terminated(&self) -> bool {
self.threads.iter().all(|thread| thread.state == ThreadState::Terminated)
}
/// Enable the thread for execution. The thread must be terminated.
fn enable_thread(&mut self, thread_id: ThreadId) {
assert!(self.has_terminated(thread_id));
@ -491,15 +496,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
// If we get here again and the thread is *still* terminated, there are no more dtors to run.
if self.threads[MAIN_THREAD].state == ThreadState::Terminated {
// The main thread terminated; stop the program.
if self.threads.iter().any(|thread| thread.state != ThreadState::Terminated) {
// FIXME: This check should be either configurable or just emit
// a warning. For example, it seems normal for a program to
// terminate without waiting for its detached threads to
// terminate. However, this case is not trivial to support
// because we also probably do not want to consider the memory
// owned by these threads as leaked.
throw_unsup_format!("the main thread terminated without waiting for other threads");
}
// We do *not* run TLS dtors of remaining threads, which seems to match rustc behavior.
return Ok(SchedulingAction::Stop);
}
// This thread and the program can keep going.
@ -645,6 +642,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.machine.threads.has_terminated(thread_id)
}
#[inline]
fn have_all_terminated(&self) -> bool {
let this = self.eval_context_ref();
this.machine.threads.have_all_terminated()
}
#[inline]
fn enable_thread(&mut self, thread_id: ThreadId) {
let this = self.eval_context_mut();

View File

@ -1,5 +1,5 @@
// ignore-windows: No libc on Windows
// error-pattern: unsupported operation: the main thread terminated without waiting for other threads
// error-pattern: the main thread terminated without waiting for all remaining threads
// Check that we terminate the program when the main thread terminates.
@ -10,7 +10,7 @@ extern crate libc;
use std::{mem, ptr};
extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void {
ptr::null_mut()
loop {}
}
fn main() {

View File

@ -0,0 +1,24 @@
// compile-flags: -Zmiri-ignore-leaks
//! Test that leaking threads works, and that their destructors are not executed.
use std::cell::RefCell;
struct LoudDrop(i32);
impl Drop for LoudDrop {
fn drop(&mut self) {
eprintln!("Dropping {}", self.0);
}
}
thread_local! {
static X: RefCell<Option<LoudDrop>> = RefCell::new(None);
}
fn main() {
X.with(|x| *x.borrow_mut() = Some(LoudDrop(0)));
let _detached = std::thread::spawn(|| {
X.with(|x| *x.borrow_mut() = Some(LoudDrop(1)));
});
}

View File

@ -0,0 +1,3 @@
warning: thread support is experimental and incomplete: weak memory effects are not emulated.
Dropping 0