#![allow(improper_ctypes)]
#[macro_use]
extern crate cfg_if;
extern crate libc;
#[cfg(windows)]
extern crate windows_sys;
#[macro_use]
extern crate psm;
mod backends;
use std::cell::Cell;
#[inline(always)]
pub fn maybe_grow<R, F: FnOnce() -> R>(red_zone: usize, stack_size: usize, callback: F) -> R {
let enough_space = match remaining_stack() {
Some(remaining) => remaining >= red_zone,
None => false,
};
if enough_space {
callback()
} else {
grow(stack_size, callback)
}
}
pub fn grow<R, F: FnOnce() -> R>(stack_size: usize, callback: F) -> R {
let mut opt_callback = Some(callback);
let mut ret = None;
let ret_ref = &mut ret;
let dyn_callback: &mut dyn FnMut() = &mut || {
let taken_callback = opt_callback.take().unwrap();
*ret_ref = Some(taken_callback());
};
_grow(stack_size, dyn_callback);
ret.unwrap()
}
pub fn remaining_stack() -> Option<usize> {
let current_ptr = current_stack_ptr();
get_stack_limit().map(|limit| current_ptr.saturating_sub(limit))
}
psm_stack_information!(
yes {
fn current_stack_ptr() -> usize {
psm::stack_pointer() as usize
}
}
no {
#[inline(always)]
fn current_stack_ptr() -> usize {
unsafe {
let mut x = std::mem::MaybeUninit::<u8>::uninit();
x.as_mut_ptr().write_volatile(42);
x.as_ptr() as usize
}
}
}
);
thread_local! {
static STACK_LIMIT: Cell<Option<usize>> = Cell::new(unsafe {
backends::guess_os_stack_limit()
})
}
#[inline(always)]
fn get_stack_limit() -> Option<usize> {
STACK_LIMIT.with(|s| s.get())
}
#[inline(always)]
#[allow(unused)]
fn set_stack_limit(l: Option<usize>) {
STACK_LIMIT.with(|s| s.set(l))
}
psm_stack_manipulation! {
yes {
#[cfg(not(any(target_arch = "wasm32",target_os = "hermit")))]
#[path = "mmap_stack_restore_guard.rs"]
mod stack_restore_guard;
#[cfg(any(target_arch = "wasm32",target_os = "hermit"))]
#[path = "alloc_stack_restore_guard.rs"]
mod stack_restore_guard;
use stack_restore_guard::StackRestoreGuard;
fn _grow(requested_stack_size: usize, callback: &mut dyn FnMut()) {
unsafe {
let guard = StackRestoreGuard::new(requested_stack_size);
let (stack_base, allocated_stack_size) = guard.stack_area();
debug_assert!(allocated_stack_size >= requested_stack_size);
set_stack_limit(Some(stack_base as usize));
let panic = psm::on_stack(stack_base, requested_stack_size, move || {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(callback)).err()
});
drop(guard);
if let Some(p) = panic {
std::panic::resume_unwind(p);
}
}
}
}
no {
#[cfg(not(windows))]
fn _grow(stack_size: usize, callback: &mut dyn FnMut()) {
let _ = stack_size;
callback();
}
#[cfg(windows)]
use backends::windows::_grow;
}
}