2024-08-01 12:17:34 -05:00
|
|
|
// libtest used to panic if it hit the thread limit. This often resulted in spurious test failures
|
|
|
|
// (thread 'main' panicked at 'called Result::unwrap() on an Err value: Os
|
|
|
|
// { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" }' ...
|
|
|
|
// error: test failed, to rerun pass '--lib').
|
|
|
|
// Since the fix in #81546, the test should continue to run synchronously
|
|
|
|
// if it runs out of threads. Therefore, this test's final execution step
|
|
|
|
// should succeed without an error.
|
|
|
|
// See https://github.com/rust-lang/rust/pull/81546
|
|
|
|
|
|
|
|
//@ only-linux
|
|
|
|
// Reason: thread limit modification
|
|
|
|
//@ ignore-cross-compile
|
|
|
|
// Reason: this test fails armhf-gnu, reasons unknown
|
2024-08-28 10:01:18 -05:00
|
|
|
//@ needs-unwind
|
|
|
|
// Reason: this should be ignored in cg_clif (Cranelift) CI and anywhere
|
|
|
|
// else that uses panic=abort.
|
2024-08-01 12:17:34 -05:00
|
|
|
|
|
|
|
use std::ffi::{self, CStr, CString};
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
use run_make_support::{libc, run, rustc};
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
rustc().input("test.rs").arg("--test").run();
|
|
|
|
|
|
|
|
// We need to emulate an environment for libtest where threads are exhausted and spawning
|
|
|
|
// new threads are guaranteed to fail. This was previously achieved by ulimit shell builtin
|
|
|
|
// that called out to prlimit64 underneath to set resource limits (specifically thread
|
|
|
|
// number limits). Now that we don't have a shell, we need to implement that ourselves.
|
|
|
|
// See https://linux.die.net/man/2/setrlimit
|
|
|
|
|
|
|
|
// The fork + exec is required because we cannot first try to limit the number of
|
|
|
|
// processes/threads to 1 and then try to spawn a new process to run the test. We need to
|
|
|
|
// setrlimit and run the libtest test program in the same process.
|
|
|
|
let pid = unsafe { libc::fork() };
|
|
|
|
assert!(pid >= 0);
|
|
|
|
|
|
|
|
// If the process ID is 0, this is the child process responsible for running the test
|
|
|
|
// program.
|
|
|
|
if pid == 0 {
|
|
|
|
let test = CString::new("test").unwrap();
|
|
|
|
// The argv array should be terminated with a NULL pointer.
|
|
|
|
let argv = [test.as_ptr(), std::ptr::null()];
|
|
|
|
// rlim_cur is soft limit, rlim_max is hard limit.
|
|
|
|
// By setting the limit very low (max 1), we ensure that libtest is unable to create new
|
|
|
|
// threads.
|
|
|
|
let rlimit = libc::rlimit { rlim_cur: 1, rlim_max: 1 };
|
|
|
|
// RLIMIT_NPROC: The maximum number of processes (or, more precisely on Linux,
|
|
|
|
// threads) that can be created for the real user ID of the calling process. Upon
|
|
|
|
// encountering this limit, fork(2) fails with the error EAGAIN.
|
|
|
|
// Therefore, set the resource limit to RLIMIT_NPROC.
|
|
|
|
let ret = unsafe { libc::setrlimit(libc::RLIMIT_NPROC, &rlimit as *const libc::rlimit) };
|
|
|
|
assert_eq!(ret, 0);
|
|
|
|
|
|
|
|
// Finally, execute the 2 tests in test.rs.
|
|
|
|
let ret = unsafe { libc::execv(test.as_ptr(), argv.as_ptr()) };
|
|
|
|
assert_eq!(ret, 0);
|
|
|
|
} else {
|
|
|
|
// Otherwise, other process IDs indicate that this is the parent process.
|
|
|
|
|
|
|
|
let mut status: libc::c_int = 0;
|
|
|
|
let ret = unsafe { libc::waitpid(pid, &mut status as *mut libc::c_int, 0) };
|
|
|
|
assert_eq!(ret, pid);
|
|
|
|
assert!(libc::WIFEXITED(status));
|
|
|
|
assert_eq!(libc::WEXITSTATUS(status), 0);
|
|
|
|
}
|
|
|
|
}
|