Skip to content

Commit 91e4b4a

Browse files
committed
Auto merge of #128507 - Oneirical:testart-from-scratch, r=jieyouxu
Migrate `libtest-thread-limit` `run-make` test to rmake Part of #121876 and the associated [Google Summer of Code project](https://blog.rust-lang.org/2024/05/01/gsoc-2024-selected-projects.html). Please try, but **only if normal CI is green**: // try-job: armhf-gnu // <- failed on this try-job: aarch64-gnu
2 parents a60a9e5 + 318dfb4 commit 91e4b4a

File tree

7 files changed

+73
-9
lines changed

7 files changed

+73
-9
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3129,6 +3129,7 @@ dependencies = [
31293129
"bstr",
31303130
"build_helper",
31313131
"gimli 0.31.0",
3132+
"libc",
31323133
"object 0.36.3",
31333134
"regex",
31343135
"serde_json",

src/tools/run-make-support/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
1212
gimli = "0.31.0"
1313
build_helper = { path = "../build_helper" }
1414
serde_json = "1.0"
15+
libc = "0.2"

src/tools/run-make-support/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub mod rfs {
3636
// Re-exports of third-party library crates.
3737
pub use bstr;
3838
pub use gimli;
39+
pub use libc;
3940
pub use object;
4041
pub use regex;
4142
pub use serde_json;

src/tools/tidy/src/allowed_run_make_makefiles.txt

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ run-make/incr-add-rust-src-component/Makefile
66
run-make/issue-84395-lto-embed-bitcode/Makefile
77
run-make/jobserver-error/Makefile
88
run-make/libs-through-symlinks/Makefile
9-
run-make/libtest-thread-limit/Makefile
109
run-make/macos-deployment-target/Makefile
1110
run-make/split-debuginfo/Makefile
1211
run-make/symbol-mangling-hashed/Makefile

tests/run-make/libtest-thread-limit/Makefile

-7
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
}

tests/run-make/libtest-thread-limit/test.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ fn spawn_thread_would_block() {
1010
THREAD_ID.set(thread::current().id()).unwrap();
1111
}
1212

13+
// Tests are run in alphabetical order, and the second test is dependent on the
14+
// first to set THREAD_ID. Do not rename the tests in such a way that `test_run_in_same_thread`
15+
// would run before `spawn_thread_would_block`.
16+
// See https://doc.rust-lang.org/rustc/tests/index.html#--shuffle
17+
1318
#[test]
14-
fn run_in_same_thread() {
19+
fn test_run_in_same_thread() {
1520
assert_eq!(*THREAD_ID.get().unwrap(), thread::current().id());
1621
}

0 commit comments

Comments
 (0)