also ignore 'thread leaks' with -Zmiri-ignore-leaks
This commit is contained in:
parent
e2872a3f2a
commit
71efd950d1
@ -230,7 +230,8 @@ environment variable:
|
|||||||
the host so that it cannot be accessed by the program. Can be used multiple
|
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
|
times to exclude several variables. On Windows, the `TERM` environment
|
||||||
variable is excluded by default.
|
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.
|
* `-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.
|
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
|
The profile is written out to a file with the prefix `<name>`, and can be processed
|
||||||
|
@ -300,6 +300,12 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
|
|||||||
match res {
|
match res {
|
||||||
Ok(return_code) => {
|
Ok(return_code) => {
|
||||||
if !ignore_leaks {
|
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);
|
info!("Additonal static roots: {:?}", ecx.machine.static_roots);
|
||||||
let leaks = ecx.memory.leak_report(&ecx.machine.static_roots);
|
let leaks = ecx.memory.leak_report(&ecx.machine.static_roots);
|
||||||
if leaks != 0 {
|
if leaks != 0 {
|
||||||
|
@ -302,6 +302,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
|||||||
self.threads[thread_id].state == ThreadState::Terminated
|
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.
|
/// Enable the thread for execution. The thread must be terminated.
|
||||||
fn enable_thread(&mut self, thread_id: ThreadId) {
|
fn enable_thread(&mut self, thread_id: ThreadId) {
|
||||||
assert!(self.has_terminated(thread_id));
|
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 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 {
|
if self.threads[MAIN_THREAD].state == ThreadState::Terminated {
|
||||||
// The main thread terminated; stop the program.
|
// The main thread terminated; stop the program.
|
||||||
if self.threads.iter().any(|thread| thread.state != ThreadState::Terminated) {
|
// We do *not* run TLS dtors of remaining threads, which seems to match rustc behavior.
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
return Ok(SchedulingAction::Stop);
|
return Ok(SchedulingAction::Stop);
|
||||||
}
|
}
|
||||||
// This thread and the program can keep going.
|
// 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)
|
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]
|
#[inline]
|
||||||
fn enable_thread(&mut self, thread_id: ThreadId) {
|
fn enable_thread(&mut self, thread_id: ThreadId) {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// ignore-windows: No libc on Windows
|
// 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.
|
// Check that we terminate the program when the main thread terminates.
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ extern crate libc;
|
|||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
|
||||||
extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void {
|
extern "C" fn thread_start(_null: *mut libc::c_void) -> *mut libc::c_void {
|
||||||
ptr::null_mut()
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
24
tests/run-pass/threadleak_ignored.rs
Normal file
24
tests/run-pass/threadleak_ignored.rs
Normal 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)));
|
||||||
|
});
|
||||||
|
}
|
3
tests/run-pass/threadleak_ignored.stderr
Normal file
3
tests/run-pass/threadleak_ignored.stderr
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
warning: thread support is experimental and incomplete: weak memory effects are not emulated.
|
||||||
|
|
||||||
|
Dropping 0
|
Loading…
x
Reference in New Issue
Block a user