use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
const MARKER: &str = "RUSTY_ALLOC_EXIT_HANG_CHILD";
const EXIT_CODE: i32 = 41;
fn child() -> ! {
static HELD: AtomicBool = AtomicBool::new(false);
let p = rusty_alloc::alloc::malloc(64);
assert!(!p.is_null(), "child: malloc failed");
std::thread::spawn(|| {
rusty_alloc::init::for_each_heap(&mut |_| {
HELD.store(true, Ordering::Release);
loop {
std::thread::park();
}
});
});
while !HELD.load(Ordering::Acquire) {
std::thread::yield_now();
}
std::process::exit(EXIT_CODE);
}
#[cfg_attr(miri, ignore)]
#[test]
fn exit_with_a_lock_held_by_a_killed_thread_does_not_hang() {
if std::env::var(MARKER).is_ok() {
child();
}
let exe = std::env::current_exe().expect("current_exe");
let mut c = Command::new(exe)
.env(MARKER, "1")
.args([
"--exact",
"exit_with_a_lock_held_by_a_killed_thread_does_not_hang",
])
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.expect("spawn child");
let deadline = Instant::now() + Duration::from_secs(30);
let status = loop {
if let Some(s) = c.try_wait().expect("wait child") {
break s;
}
assert!(
Instant::now() < deadline,
"child {} did not exit in 30 s: it is spinning in thread teardown on a lock \
held by a thread ExitProcess killed (it cannot be ended with TerminateProcess)",
c.id()
);
std::thread::sleep(Duration::from_millis(50));
};
assert_eq!(status.code(), Some(EXIT_CODE), "child exited abnormally");
}