|
| 1 | +// libtest used to panic if it hit the thread limit. This often resulted in spurious test failures |
| 2 | +// (thread 'main' panicked at 'called Result::unwrap() on an Err value: Os |
| 3 | +// { code: 11, kind: WouldBlock, message: "Resource temporarily unavailable" }' ... |
| 4 | +// error: test failed, to rerun pass '--lib'). |
| 5 | +// Since the fix in #81546, the test should continue to run synchronously |
| 6 | +// if it runs out of threads. Therefore, this test's final execution step |
| 7 | +// should succeed without an error. |
| 8 | +// See https://github.com/rust-lang/rust/pull/81546 |
| 9 | + |
| 10 | +//@ only-linux |
| 11 | +// Reason: thread limit modification |
| 12 | +//@ ignore-cross-compile |
| 13 | +// Reason: this test fails armhf-gnu, reasons unknown |
| 14 | + |
| 15 | +use std::ffi::{self, CStr, CString}; |
| 16 | +use std::path::PathBuf; |
| 17 | + |
| 18 | +use run_make_support::{libc, run, rustc}; |
| 19 | + |
| 20 | +fn main() { |
| 21 | + rustc().input("test.rs").arg("--test").run(); |
| 22 | + |
| 23 | + // We need to emulate an environment for libtest where threads are exhausted and spawning |
| 24 | + // new threads are guaranteed to fail. This was previously achieved by ulimit shell builtin |
| 25 | + // that called out to prlimit64 underneath to set resource limits (specifically thread |
| 26 | + // number limits). Now that we don't have a shell, we need to implement that ourselves. |
| 27 | + // See https://linux.die.net/man/2/setrlimit |
| 28 | + |
| 29 | + // The fork + exec is required because we cannot first try to limit the number of |
| 30 | + // processes/threads to 1 and then try to spawn a new process to run the test. We need to |
| 31 | + // setrlimit and run the libtest test program in the same process. |
| 32 | + let pid = unsafe { libc::fork() }; |
| 33 | + assert!(pid >= 0); |
| 34 | + |
| 35 | + // If the process ID is 0, this is the child process responsible for running the test |
| 36 | + // program. |
| 37 | + if pid == 0 { |
| 38 | + let test = CString::new("test").unwrap(); |
| 39 | + // The argv array should be terminated with a NULL pointer. |
| 40 | + let argv = [test.as_ptr(), std::ptr::null()]; |
| 41 | + // rlim_cur is soft limit, rlim_max is hard limit. |
| 42 | + // By setting the limit very low (max 1), we ensure that libtest is unable to create new |
| 43 | + // threads. |
| 44 | + let rlimit = libc::rlimit { rlim_cur: 1, rlim_max: 1 }; |
| 45 | + // RLIMIT_NPROC: The maximum number of processes (or, more precisely on Linux, |
| 46 | + // threads) that can be created for the real user ID of the calling process. Upon |
| 47 | + // encountering this limit, fork(2) fails with the error EAGAIN. |
| 48 | + // Therefore, set the resource limit to RLIMIT_NPROC. |
| 49 | + let ret = unsafe { libc::setrlimit(libc::RLIMIT_NPROC, &rlimit as *const libc::rlimit) }; |
| 50 | + assert_eq!(ret, 0); |
| 51 | + |
| 52 | + // Finally, execute the 2 tests in test.rs. |
| 53 | + let ret = unsafe { libc::execv(test.as_ptr(), argv.as_ptr()) }; |
| 54 | + assert_eq!(ret, 0); |
| 55 | + } else { |
| 56 | + // Otherwise, other process IDs indicate that this is the parent process. |
| 57 | + |
| 58 | + let mut status: libc::c_int = 0; |
| 59 | + let ret = unsafe { libc::waitpid(pid, &mut status as *mut libc::c_int, 0) }; |
| 60 | + assert_eq!(ret, pid); |
| 61 | + assert!(libc::WIFEXITED(status)); |
| 62 | + assert_eq!(libc::WEXITSTATUS(status), 0); |
| 63 | + } |
| 64 | +} |
0 commit comments