use std::{env, sync::OnceLock, thread};
static SYSTEM_THREAD_STACK_SIZE: OnceLock<usize> = OnceLock::new();
const RUST_DEFAULT_THREAD_STACK_SIZE: usize = 2 * 1024 * 1024;
fn rust_min_stack() -> Option<usize> {
env::var_os("RUST_MIN_STACK").and_then(|s| s.to_str().and_then(|s| s.parse().ok()))
}
pub(crate) fn system_thread_stack_size() -> usize {
*SYSTEM_THREAD_STACK_SIZE.get_or_init(|| {
rust_min_stack()
.or(system_thread_stack_size_impl())
.unwrap_or(RUST_DEFAULT_THREAD_STACK_SIZE)
})
}
#[cfg(all(unix, not(target_os = "macos")))]
fn system_thread_stack_size_impl() -> Option<usize> {
let mut attr = std::mem::MaybeUninit::<libc::pthread_attr_t>::uninit();
if unsafe { libc::pthread_attr_init(attr.as_mut_ptr()) } != 0 {
return None;
}
let mut attr = unsafe { attr.assume_init() };
let mut stack_size = 0;
let get_result = unsafe { libc::pthread_attr_getstacksize(&attr, &mut stack_size) };
let destroy_result = unsafe { libc::pthread_attr_destroy(&mut attr) };
if get_result != 0 || destroy_result != 0 || stack_size == 0 {
return None;
}
Some(stack_size)
}
#[cfg(target_os = "macos")]
fn system_thread_stack_size_impl() -> Option<usize> {
let mut stack_limit = std::mem::MaybeUninit::<libc::rlimit>::uninit();
let limit_result = unsafe { libc::getrlimit(libc::RLIMIT_STACK, stack_limit.as_mut_ptr()) };
if limit_result != 0 {
return None;
}
let stack_limit = unsafe { stack_limit.assume_init() };
if stack_limit.rlim_cur == libc::RLIM_INFINITY {
return None;
}
let limit = usize::try_from(stack_limit.rlim_cur).ok()?;
if limit == 0 {
return None;
}
Some(limit)
}
#[cfg(not(unix))]
const fn system_thread_stack_size_impl() -> Option<usize> {
None
}
pub(crate) fn spawn<F, T>(stack_size: usize, f: F) -> thread::JoinHandle<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
thread::Builder::new()
.stack_size(stack_size)
.spawn(f)
.expect("failed to spawn thread")
}