Skip to content

Commit 7d65f5f

Browse files
committed
fix windows build
1 parent 661b209 commit 7d65f5f

File tree

9 files changed

+141
-33
lines changed

9 files changed

+141
-33
lines changed
File renamed without changes.
File renamed without changes.

src/guess_os_stack_limit/mod.rs renamed to src/backends/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ cfg_if! {
33
mod fallback;
44
pub use fallback::guess_os_stack_limit;
55
} else if #[cfg(windows)] {
6-
mod windows;
6+
pub(crate) mod windows;
77
pub use windows::guess_os_stack_limit;
88
} else if #[cfg(any(
99
target_os = "linux",
File renamed without changes.
File renamed without changes.

src/backends/windows.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use libc::c_void;
2+
use std::io;
3+
use std::ptr;
4+
use windows_sys::Win32::Foundation::BOOL;
5+
use windows_sys::Win32::System::Memory::VirtualQuery;
6+
use windows_sys::Win32::System::Threading::{
7+
ConvertFiberToThread, ConvertThreadToFiber, CreateFiber, DeleteFiber, IsThreadAFiber,
8+
SetThreadStackGuarantee, SwitchToFiber,
9+
};
10+
11+
// Make sure the libstacker.a (implemented in C) is linked.
12+
// See https://github.com/rust-lang/rust/issues/65610
13+
#[link(name = "stacker")]
14+
extern "C" {
15+
fn __stacker_get_current_fiber() -> *mut c_void;
16+
}
17+
18+
struct FiberInfo<F> {
19+
callback: std::mem::MaybeUninit<F>,
20+
panic: Option<Box<dyn std::any::Any + Send + 'static>>,
21+
parent_fiber: *mut c_void,
22+
}
23+
24+
unsafe extern "system" fn fiber_proc<F: FnOnce()>(data: *mut c_void) {
25+
// This function is the entry point to our inner fiber, and as argument we get an
26+
// instance of `FiberInfo`. We will set-up the "runtime" for the callback and execute
27+
// it.
28+
let data = &mut *(data as *mut FiberInfo<F>);
29+
let old_stack_limit = crate::get_stack_limit();
30+
crate::set_stack_limit(guess_os_stack_limit());
31+
let callback = data.callback.as_ptr();
32+
data.panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(callback.read())).err();
33+
34+
// Restore to the previous Fiber
35+
crate::set_stack_limit(old_stack_limit);
36+
SwitchToFiber(data.parent_fiber);
37+
}
38+
39+
pub fn _grow(stack_size: usize, callback: &mut dyn FnMut()) {
40+
// Fibers (or stackful coroutines) is the only official way to create new stacks on the
41+
// same thread on Windows. So in order to extend the stack we create fiber and switch
42+
// to it so we can use it's stack. After running `callback` within our fiber, we switch
43+
// back to the current stack and destroy the fiber and its associated stack.
44+
unsafe {
45+
let was_fiber = IsThreadAFiber() == 1 as BOOL;
46+
let mut data = FiberInfo {
47+
callback: std::mem::MaybeUninit::new(callback),
48+
panic: None,
49+
parent_fiber: {
50+
if was_fiber {
51+
// Get a handle to the current fiber. We need to use a C implementation
52+
// for this as GetCurrentFiber is an header only function.
53+
__stacker_get_current_fiber()
54+
} else {
55+
// Convert the current thread to a fiber, so we are able to switch back
56+
// to the current stack. Threads coverted to fibers still act like
57+
// regular threads, but they have associated fiber data. We later
58+
// convert it back to a regular thread and free the fiber data.
59+
ConvertThreadToFiber(ptr::null_mut())
60+
}
61+
},
62+
};
63+
64+
if data.parent_fiber.is_null() {
65+
panic!(
66+
"unable to convert thread to fiber: {}",
67+
io::Error::last_os_error()
68+
);
69+
}
70+
71+
let fiber = CreateFiber(
72+
stack_size as usize,
73+
Some(fiber_proc::<&mut dyn FnMut()>),
74+
&mut data as *mut FiberInfo<&mut dyn FnMut()> as *mut _,
75+
);
76+
if fiber.is_null() {
77+
panic!("unable to allocate fiber: {}", io::Error::last_os_error());
78+
}
79+
80+
// Switch to the fiber we created. This changes stacks and starts executing
81+
// fiber_proc on it. fiber_proc will run `callback` and then switch back to run the
82+
// next statement.
83+
SwitchToFiber(fiber);
84+
DeleteFiber(fiber);
85+
86+
// Clean-up.
87+
if !was_fiber && ConvertFiberToThread() == 0 {
88+
// FIXME: Perhaps should not panic here?
89+
panic!(
90+
"unable to convert back to thread: {}",
91+
io::Error::last_os_error()
92+
);
93+
}
94+
95+
if let Some(p) = data.panic {
96+
std::panic::resume_unwind(p);
97+
}
98+
}
99+
}
100+
101+
#[inline(always)]
102+
fn get_thread_stack_guarantee() -> usize {
103+
let min_guarantee = if cfg!(target_pointer_width = "32") {
104+
0x1000
105+
} else {
106+
0x2000
107+
};
108+
let mut stack_guarantee = 0;
109+
unsafe {
110+
// Read the current thread stack guarantee
111+
// This is the stack reserved for stack overflow
112+
// exception handling.
113+
// This doesn't return the true value so we need
114+
// some further logic to calculate the real stack
115+
// guarantee. This logic is what is used on x86-32 and
116+
// x86-64 Windows 10. Other versions and platforms may differ
117+
SetThreadStackGuarantee(&mut stack_guarantee)
118+
};
119+
std::cmp::max(stack_guarantee, min_guarantee) as usize + 0x1000
120+
}
121+
122+
#[inline(always)]
123+
pub unsafe fn guess_os_stack_limit() -> Option<usize> {
124+
// Query the allocation which contains our stack pointer in order
125+
// to discover the size of the stack
126+
//
127+
// FIXME: we could read stack base from the TIB, specifically the 3rd element of it.
128+
type QueryT = windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION;
129+
let mut mi = std::mem::MaybeUninit::<QueryT>::uninit();
130+
VirtualQuery(
131+
psm::stack_pointer() as *const _,
132+
mi.as_mut_ptr(),
133+
std::mem::size_of::<QueryT>() as usize,
134+
);
135+
Some(mi.assume_init().AllocationBase as usize + get_thread_stack_guarantee() + 0x1000)
136+
}

src/guess_os_stack_limit/windows.rs

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extern crate windows_sys;
3232
#[macro_use]
3333
extern crate psm;
3434

35-
mod guess_os_stack_limit;
35+
mod backends;
3636

3737
use std::cell::Cell;
3838

@@ -118,7 +118,7 @@ psm_stack_information!(
118118

119119
thread_local! {
120120
static STACK_LIMIT: Cell<Option<usize>> = Cell::new(unsafe {
121-
guess_os_stack_limit::guess_os_stack_limit()
121+
backends::guess_os_stack_limit()
122122
})
123123
}
124124

@@ -276,5 +276,7 @@ psm_stack_manipulation! {
276276
let _ = stack_size;
277277
callback();
278278
}
279+
#[cfg(windows)]
280+
use backends::windows::_grow;
279281
}
280282
}

0 commit comments

Comments
 (0)