-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
141 additions
and
33 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
use libc::c_void; | ||
use std::io; | ||
use std::ptr; | ||
use windows_sys::Win32::Foundation::BOOL; | ||
use windows_sys::Win32::System::Memory::VirtualQuery; | ||
use windows_sys::Win32::System::Threading::{ | ||
ConvertFiberToThread, ConvertThreadToFiber, CreateFiber, DeleteFiber, IsThreadAFiber, | ||
SetThreadStackGuarantee, SwitchToFiber, | ||
}; | ||
|
||
// Make sure the libstacker.a (implemented in C) is linked. | ||
// See https://github.com/rust-lang/rust/issues/65610 | ||
#[link(name = "stacker")] | ||
extern "C" { | ||
fn __stacker_get_current_fiber() -> *mut c_void; | ||
} | ||
|
||
struct FiberInfo<F> { | ||
callback: std::mem::MaybeUninit<F>, | ||
panic: Option<Box<dyn std::any::Any + Send + 'static>>, | ||
parent_fiber: *mut c_void, | ||
} | ||
|
||
unsafe extern "system" fn fiber_proc<F: FnOnce()>(data: *mut c_void) { | ||
// This function is the entry point to our inner fiber, and as argument we get an | ||
// instance of `FiberInfo`. We will set-up the "runtime" for the callback and execute | ||
// it. | ||
let data = &mut *(data as *mut FiberInfo<F>); | ||
let old_stack_limit = crate::get_stack_limit(); | ||
crate::set_stack_limit(guess_os_stack_limit()); | ||
let callback = data.callback.as_ptr(); | ||
data.panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(callback.read())).err(); | ||
|
||
// Restore to the previous Fiber | ||
crate::set_stack_limit(old_stack_limit); | ||
SwitchToFiber(data.parent_fiber); | ||
} | ||
|
||
pub fn _grow(stack_size: usize, callback: &mut dyn FnMut()) { | ||
// Fibers (or stackful coroutines) is the only official way to create new stacks on the | ||
// same thread on Windows. So in order to extend the stack we create fiber and switch | ||
// to it so we can use it's stack. After running `callback` within our fiber, we switch | ||
// back to the current stack and destroy the fiber and its associated stack. | ||
unsafe { | ||
let was_fiber = IsThreadAFiber() == 1 as BOOL; | ||
let mut data = FiberInfo { | ||
callback: std::mem::MaybeUninit::new(callback), | ||
panic: None, | ||
parent_fiber: { | ||
if was_fiber { | ||
// Get a handle to the current fiber. We need to use a C implementation | ||
// for this as GetCurrentFiber is an header only function. | ||
__stacker_get_current_fiber() | ||
} else { | ||
// Convert the current thread to a fiber, so we are able to switch back | ||
// to the current stack. Threads coverted to fibers still act like | ||
// regular threads, but they have associated fiber data. We later | ||
// convert it back to a regular thread and free the fiber data. | ||
ConvertThreadToFiber(ptr::null_mut()) | ||
} | ||
}, | ||
}; | ||
|
||
if data.parent_fiber.is_null() { | ||
panic!( | ||
"unable to convert thread to fiber: {}", | ||
io::Error::last_os_error() | ||
); | ||
} | ||
|
||
let fiber = CreateFiber( | ||
stack_size as usize, | ||
Some(fiber_proc::<&mut dyn FnMut()>), | ||
&mut data as *mut FiberInfo<&mut dyn FnMut()> as *mut _, | ||
); | ||
if fiber.is_null() { | ||
panic!("unable to allocate fiber: {}", io::Error::last_os_error()); | ||
} | ||
|
||
// Switch to the fiber we created. This changes stacks and starts executing | ||
// fiber_proc on it. fiber_proc will run `callback` and then switch back to run the | ||
// next statement. | ||
SwitchToFiber(fiber); | ||
DeleteFiber(fiber); | ||
|
||
// Clean-up. | ||
if !was_fiber && ConvertFiberToThread() == 0 { | ||
// FIXME: Perhaps should not panic here? | ||
panic!( | ||
"unable to convert back to thread: {}", | ||
io::Error::last_os_error() | ||
); | ||
} | ||
|
||
if let Some(p) = data.panic { | ||
std::panic::resume_unwind(p); | ||
} | ||
} | ||
} | ||
|
||
#[inline(always)] | ||
fn get_thread_stack_guarantee() -> usize { | ||
let min_guarantee = if cfg!(target_pointer_width = "32") { | ||
0x1000 | ||
} else { | ||
0x2000 | ||
}; | ||
let mut stack_guarantee = 0; | ||
unsafe { | ||
// Read the current thread stack guarantee | ||
// This is the stack reserved for stack overflow | ||
// exception handling. | ||
// This doesn't return the true value so we need | ||
// some further logic to calculate the real stack | ||
// guarantee. This logic is what is used on x86-32 and | ||
// x86-64 Windows 10. Other versions and platforms may differ | ||
SetThreadStackGuarantee(&mut stack_guarantee) | ||
}; | ||
std::cmp::max(stack_guarantee, min_guarantee) as usize + 0x1000 | ||
} | ||
|
||
#[inline(always)] | ||
pub unsafe fn guess_os_stack_limit() -> Option<usize> { | ||
// Query the allocation which contains our stack pointer in order | ||
// to discover the size of the stack | ||
// | ||
// FIXME: we could read stack base from the TIB, specifically the 3rd element of it. | ||
type QueryT = windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION; | ||
let mut mi = std::mem::MaybeUninit::<QueryT>::uninit(); | ||
VirtualQuery( | ||
psm::stack_pointer() as *const _, | ||
mi.as_mut_ptr(), | ||
std::mem::size_of::<QueryT>() as usize, | ||
); | ||
Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000) | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters