diff options
Diffstat (limited to 'vendor/stacker/src/lib.rs')
| -rw-r--r-- | vendor/stacker/src/lib.rs | 181 |
1 files changed, 0 insertions, 181 deletions
diff --git a/vendor/stacker/src/lib.rs b/vendor/stacker/src/lib.rs deleted file mode 100644 index ee5803c3..00000000 --- a/vendor/stacker/src/lib.rs +++ /dev/null @@ -1,181 +0,0 @@ -//! A library to help grow the stack when it runs out of space. -//! -//! This is an implementation of manually instrumented segmented stacks where points in a program's -//! control flow are annotated with "maybe grow the stack here". Each point of annotation indicates -//! how far away from the end of the stack it's allowed to be, plus the amount of stack to allocate -//! if it does reach the end. -//! -//! Once a program has reached the end of its stack, a temporary stack on the heap is allocated and -//! is switched to for the duration of a closure. -//! -//! For a set of lower-level primitives, consider the `psm` crate. -//! -//! # Examples -//! -//! ``` -//! // Grow the stack if we are within the "red zone" of 32K, and if we allocate -//! // a new stack allocate 1MB of stack space. -//! // -//! // If we're already in bounds, just run the provided closure on current stack. -//! stacker::maybe_grow(32 * 1024, 1024 * 1024, || { -//! // guaranteed to have at least 32K of stack -//! }); -//! ``` - -#![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; - -/// Grows the call stack if necessary. -/// -/// This function is intended to be called at manually instrumented points in a program where -/// recursion is known to happen quite a bit. This function will check to see if we're within -/// `red_zone` bytes of the end of the stack, and if so it will allocate a new stack of at least -/// `stack_size` bytes. -/// -/// The closure `f` is guaranteed to run on a stack with at least `red_zone` bytes, and it will be -/// run on the current stack if there's space available. -#[inline(always)] -pub fn maybe_grow<R, F: FnOnce() -> R>(red_zone: usize, stack_size: usize, callback: F) -> R { - // if we can't guess the remaining stack (unsupported on some platforms) we immediately grow - // the stack and then cache the new stack size (which we do know now because we allocated it. - let enough_space = match remaining_stack() { - Some(remaining) => remaining >= red_zone, - None => false, - }; - if enough_space { - callback() - } else { - grow(stack_size, callback) - } -} - -/// Always creates a new stack for the passed closure to run on. -/// The closure will still be on the same thread as the caller of `grow`. -/// This will allocate a new stack with at least `stack_size` bytes. -pub fn grow<R, F: FnOnce() -> R>(stack_size: usize, callback: F) -> R { - // To avoid monomorphizing `_grow()` and everything it calls, - // we convert the generic callback to a dynamic one. - let mut opt_callback = Some(callback); - let mut ret = None; - let ret_ref = &mut ret; - - // This wrapper around `callback` achieves two things: - // * It converts the `impl FnOnce` to a `dyn FnMut`. - // `dyn` because we want it to not be generic, and - // `FnMut` because we can't pass a `dyn FnOnce` around without boxing it. - // * It eliminates the generic return value, by writing it to the stack of this function. - // Otherwise the closure would have to return an unsized value, which isn't possible. - 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() -} - -/// Queries the amount of remaining stack as interpreted by this library. -/// -/// This function will return the amount of stack space left which will be used -/// to determine whether a stack switch should be made or not. -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(); - // Unlikely to be ever exercised. As a fallback we execute a volatile read to a - // local (to hopefully defeat the optimisations that would make this local a static - // global) and take its address. This way we get a very approximate address of the - // current frame. - 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()) { - // Other than that this code has no meaningful gotchas. - unsafe { - // We use a guard pattern to ensure we deallocate the allocated stack when we leave - // this function and also try to uphold various safety invariants required by `psm` - // (such as not unwinding from the callback we pass to it). - // `StackRestoreGuard` allocates a memory area with suitable size and alignment. - // It also sets up stack guards if supported on target. - 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)); - // TODO should we not pass `allocated_stack_size` here? - 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; - } -} |
