| // 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 |
| //@ needs-unwind |
| // Reason: this should be ignored in cg_clif (Cranelift) CI and anywhere |
| // else that uses panic=abort. |
| |
| 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); |
| } |
| } |