extern crate std;
use std::io::{Error, Result};
use std::ptr;
use windows_sys::Win32::System::Memory::{
VirtualAlloc, VirtualFree, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_GUARD, PAGE_READWRITE,
};
use windows_sys::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO};
use windows_sys::Win32::System::Threading::SetThreadStackGuarantee;
use super::{Stack, StackPointer, StackTebFields, MIN_STACK_SIZE};
fn page_size() -> usize {
unsafe {
let mut sysinfo: SYSTEM_INFO = std::mem::zeroed();
GetSystemInfo(&mut sysinfo);
assert!(sysinfo.dwPageSize.is_power_of_two());
sysinfo.dwPageSize as usize
}
}
fn page_round_up(val: usize, page_size: usize) -> usize {
(val + page_size - 1) & !(page_size - 1)
}
fn get_thread_stack_guarantee(page_size: usize) -> usize {
let mut stack_guarantee = 0;
unsafe {
SetThreadStackGuarantee(&mut stack_guarantee);
}
page_round_up((stack_guarantee as usize).max(page_size), page_size)
}
fn guard_page_size(page_size: usize) -> usize {
if cfg!(target_pointer_width = "64") {
2 * page_size
} else {
page_size
}
}
pub struct DefaultStack {
base: StackPointer,
limit: usize,
deallocation_stack: StackPointer,
stack_guarantee: usize,
}
impl DefaultStack {
pub fn new(size: usize) -> Result<Self> {
let size = size.max(MIN_STACK_SIZE);
let page_size = page_size();
let guard_size = guard_page_size(page_size);
let stack_guarantee = get_thread_stack_guarantee(page_size);
let extra_pages = guard_size + stack_guarantee + page_size;
let alloc_len = size
.checked_add(extra_pages + page_size - 1)
.expect("integer overflow while calculating stack size")
& !(page_size - 1);
unsafe {
let alloc_base = VirtualAlloc(ptr::null(), alloc_len, MEM_RESERVE, PAGE_READWRITE);
if alloc_base.is_null() {
return Err(Error::last_os_error());
}
let alloc_top = alloc_base as usize + alloc_len;
let limit = alloc_top - page_round_up(MIN_STACK_SIZE, page_size);
let out = Self {
base: StackPointer::new(alloc_top).unwrap(),
limit,
deallocation_stack: StackPointer::new(alloc_base as usize).unwrap(),
stack_guarantee,
};
if VirtualAlloc(
limit as *mut _,
alloc_top - limit,
MEM_COMMIT,
PAGE_READWRITE,
)
.is_null()
{
return Err(Error::last_os_error());
}
let stack_guard_size = guard_size + stack_guarantee;
if VirtualAlloc(
(limit - stack_guard_size) as *mut _,
stack_guard_size,
MEM_COMMIT,
PAGE_READWRITE | PAGE_GUARD,
)
.is_null()
{
return Err(Error::last_os_error());
}
Ok(out)
}
}
}
impl Default for DefaultStack {
fn default() -> Self {
Self::new(1024 * 1024).expect("failed to allocate stack")
}
}
impl Drop for DefaultStack {
fn drop(&mut self) {
unsafe {
let alloc_base = self.deallocation_stack.get() as *mut _;
let ret = VirtualFree(alloc_base, 0, MEM_RELEASE);
debug_assert!(ret != 0);
}
}
}
unsafe impl Stack for DefaultStack {
#[inline]
fn base(&self) -> StackPointer {
self.base
}
#[inline]
fn limit(&self) -> StackPointer {
self.deallocation_stack
}
#[inline]
fn teb_fields(&self) -> StackTebFields {
StackTebFields {
StackBase: self.base.get(),
StackLimit: self.limit,
DeallocationStack: self.deallocation_stack.get(),
GuaranteedStackBytes: self.stack_guarantee,
}
}
#[inline]
fn update_teb_fields(&mut self, stack_limit: usize, guaranteed_stack_bytes: usize) {
self.limit = stack_limit;
self.stack_guarantee = guaranteed_stack_bytes;
}
}