| 1 | use rayon_core::ThreadPoolBuilder; |
| 2 | |
| 3 | use std::env; |
| 4 | use std::process::{Command, ExitStatus, Stdio}; |
| 5 | |
| 6 | #[cfg (target_os = "linux" )] |
| 7 | use std::os::unix::process::ExitStatusExt; |
| 8 | |
| 9 | fn force_stack_overflow(depth: u32) { |
| 10 | let mut buffer = [0u8; 1024 * 1024]; |
| 11 | std::hint::black_box(&mut buffer); |
| 12 | if depth > 0 { |
| 13 | force_stack_overflow(depth - 1); |
| 14 | } |
| 15 | } |
| 16 | |
| 17 | #[cfg (unix)] |
| 18 | fn disable_core() { |
| 19 | unsafe { |
| 20 | libc::setrlimit( |
| 21 | libc::RLIMIT_CORE, |
| 22 | &libc::rlimit { |
| 23 | rlim_cur: 0, |
| 24 | rlim_max: 0, |
| 25 | }, |
| 26 | ); |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | #[cfg (unix)] |
| 31 | fn overflow_code() -> Option<i32> { |
| 32 | None |
| 33 | } |
| 34 | |
| 35 | #[cfg (windows)] |
| 36 | fn overflow_code() -> Option<i32> { |
| 37 | use std::os::windows::process::ExitStatusExt; |
| 38 | |
| 39 | ExitStatus::from_raw(0xc00000fd /*STATUS_STACK_OVERFLOW*/).code() |
| 40 | } |
| 41 | |
| 42 | #[test] |
| 43 | #[cfg_attr (not(any(unix, windows)), ignore)] |
| 44 | fn stack_overflow_crash() { |
| 45 | // First check that the recursive call actually causes a stack overflow, |
| 46 | // and does not get optimized away. |
| 47 | let status = run_ignored("run_with_small_stack" ); |
| 48 | assert!(!status.success()); |
| 49 | #[cfg (any(unix, windows))] |
| 50 | assert_eq!(status.code(), overflow_code()); |
| 51 | #[cfg (target_os = "linux" )] |
| 52 | assert!(matches!( |
| 53 | status.signal(), |
| 54 | Some(libc::SIGABRT | libc::SIGSEGV) |
| 55 | )); |
| 56 | |
| 57 | // Now run with a larger stack and verify correct operation. |
| 58 | let status = run_ignored("run_with_large_stack" ); |
| 59 | assert_eq!(status.code(), Some(0)); |
| 60 | #[cfg (target_os = "linux" )] |
| 61 | assert_eq!(status.signal(), None); |
| 62 | } |
| 63 | |
| 64 | fn run_ignored(test: &str) -> ExitStatus { |
| 65 | Command::new(env::current_exe().unwrap()) |
| 66 | .arg("--ignored" ) |
| 67 | .arg("--exact" ) |
| 68 | .arg(test) |
| 69 | .stdout(Stdio::null()) |
| 70 | .stderr(Stdio::null()) |
| 71 | .status() |
| 72 | .unwrap() |
| 73 | } |
| 74 | |
| 75 | #[test] |
| 76 | #[ignore ] |
| 77 | fn run_with_small_stack() { |
| 78 | run_with_stack(8); |
| 79 | } |
| 80 | |
| 81 | #[test] |
| 82 | #[ignore ] |
| 83 | fn run_with_large_stack() { |
| 84 | run_with_stack(48); |
| 85 | } |
| 86 | |
| 87 | fn run_with_stack(stack_size_in_mb: usize) { |
| 88 | let pool = ThreadPoolBuilder::new() |
| 89 | .stack_size(stack_size_in_mb * 1024 * 1024) |
| 90 | .build() |
| 91 | .unwrap(); |
| 92 | pool.install(|| { |
| 93 | #[cfg (unix)] |
| 94 | disable_core(); |
| 95 | force_stack_overflow(32); |
| 96 | }); |
| 97 | } |
| 98 | |