diff --git a/build.rs b/build.rs index 5f3119603..f1b0a3046 100644 --- a/build.rs +++ b/build.rs @@ -73,6 +73,7 @@ fn detect_cfgs(target: &mut Target) { ), ("bsd", &detect_bsd), ("gettext", &have_gettext), + ("small_main_stack", &has_small_stack), // See if libc supports the thread-safe localeconv_l(3) alternative to localeconv(3). ("localeconv_l", &|target| { Ok(target.has_symbol("localeconv_l")) @@ -169,6 +170,36 @@ fn have_gettext(target: &Target) -> Result> { } } +/// Rust sets the stack size of newly created threads to a sane value, but is at at the mercy of the +/// OS when it comes to the size of the main stack. Some platforms we support default to a tiny +/// 0.5 MiB main stack, which is insufficient for fish's MAX_EVAL_DEPTH/MAX_STACK_DEPTH values. +/// +/// 0.5 MiB is small enough that we'd have to drastically reduce MAX_STACK_DEPTH to less than 10, so +/// we instead use a workaround to increase the main thread size. +fn has_small_stack(_: &Target) -> Result> { + #[cfg(not(target_os = "macos"))] + return Ok(false); + + #[cfg(target_os = "macos")] + { + use core::ffi; + + extern "C" { + fn pthread_get_stacksize_np(thread: *const ffi::c_void) -> usize; + fn pthread_self() -> *const ffi::c_void; + } + + // build.rs is executed on the main thread, so we are getting the main thread's stack size. + // Modern macOS versions default to an 8 MiB main stack but legacy OS X have a 0.5 MiB one. + let stack_size = unsafe { pthread_get_stacksize_np(pthread_self()) }; + const TWO_MIB: usize = 2 * 1024 * 1024; + match stack_size { + 0..TWO_MIB => Ok(true), + _ => Ok(false), + } + } +} + fn setup_paths() { fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf { let mut var = PathBuf::from(env::var(name).unwrap_or(default.to_string())); diff --git a/src/bin/fish.rs b/src/bin/fish.rs index 51c24b725..837b15842 100644 --- a/src/bin/fish.rs +++ b/src/bin/fish.rs @@ -461,7 +461,14 @@ fn cstr_from_osstr(s: &OsStr) -> CString { fn main() { PROGRAM_NAME.set(L!("fish")).unwrap(); - panic_handler(throwing_main) + if !cfg!(small_main_stack) { + panic_handler(throwing_main); + } else { + // Create a new thread with a decent stack size to be our main thread + std::thread::scope(|scope| { + scope.spawn(|| panic_handler(throwing_main)); + }) + } } fn throwing_main() -> i32 {