Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/fiber/src/windows.rs
3064 views
1
use crate::{RunResult, RuntimeFiberStack};
2
use alloc::boxed::Box;
3
use std::cell::Cell;
4
use std::ffi::c_void;
5
use std::io;
6
use std::mem::needs_drop;
7
use std::ops::Range;
8
use std::ptr;
9
use windows_sys::Win32::Foundation::*;
10
use windows_sys::Win32::System::Threading::*;
11
12
pub type Error = io::Error;
13
14
#[derive(Debug)]
15
pub struct FiberStack(usize);
16
17
impl FiberStack {
18
pub fn new(size: usize, zeroed: bool) -> io::Result<Self> {
19
// We don't support fiber stack zeroing on windows.
20
let _ = zeroed;
21
22
Ok(Self(size))
23
}
24
25
pub unsafe fn from_raw_parts(
26
_base: *mut u8,
27
_guard_size: usize,
28
_len: usize,
29
) -> io::Result<Self> {
30
Err(io::Error::from_raw_os_error(ERROR_NOT_SUPPORTED as i32))
31
}
32
33
pub fn is_from_raw_parts(&self) -> bool {
34
false
35
}
36
37
pub fn from_custom(_custom: Box<dyn RuntimeFiberStack>) -> io::Result<Self> {
38
Err(io::Error::from_raw_os_error(ERROR_NOT_SUPPORTED as i32))
39
}
40
41
pub fn top(&self) -> Option<*mut u8> {
42
None
43
}
44
45
pub fn range(&self) -> Option<Range<usize>> {
46
None
47
}
48
49
pub fn guard_range(&self) -> Option<Range<*mut u8>> {
50
None
51
}
52
}
53
54
pub struct Fiber {
55
fiber: *mut c_void,
56
state: Box<StartState>,
57
}
58
59
pub struct Suspend {
60
state: *const StartState,
61
}
62
63
struct StartState {
64
parent: Cell<*mut c_void>,
65
initial_closure: Cell<*mut u8>,
66
result_location: Cell<*const u8>,
67
}
68
69
const FIBER_FLAG_FLOAT_SWITCH: u32 = 1;
70
71
unsafe extern "C" {
72
#[wasmtime_versioned_export_macros::versioned_link]
73
fn wasmtime_fiber_get_current() -> *mut c_void;
74
}
75
76
unsafe extern "system" fn fiber_start<F, A, B, C>(data: *mut c_void)
77
where
78
F: FnOnce(A, &mut super::Suspend<A, B, C>) -> C,
79
{
80
unsafe {
81
// Set the stack guarantee to be consistent with what Rust expects for threads
82
// This value is taken from:
83
// https://github.com/rust-lang/rust/blob/0d97f7a96877a96015d70ece41ad08bb7af12377/library/std/src/sys/windows/stack_overflow.rs
84
if SetThreadStackGuarantee(&mut 0x5000) == 0 {
85
panic!("failed to set fiber stack guarantee");
86
}
87
88
let state = data.cast::<StartState>();
89
let func = Box::from_raw((*state).initial_closure.get().cast::<F>());
90
(*state).initial_closure.set(ptr::null_mut());
91
let suspend = Suspend { state };
92
let initial = suspend.take_resume::<A, B, C>();
93
super::Suspend::<A, B, C>::execute(suspend, initial, *func);
94
}
95
}
96
97
impl Fiber {
98
pub fn new<F, A, B, C>(stack: &FiberStack, func: F) -> io::Result<Self>
99
where
100
F: FnOnce(A, &mut super::Suspend<A, B, C>) -> C,
101
{
102
unsafe {
103
let state = Box::new(StartState {
104
initial_closure: Cell::new(Box::into_raw(Box::new(func)).cast()),
105
parent: Cell::new(ptr::null_mut()),
106
result_location: Cell::new(ptr::null()),
107
});
108
109
let fiber = CreateFiberEx(
110
0,
111
stack.0,
112
FIBER_FLAG_FLOAT_SWITCH,
113
Some(fiber_start::<F, A, B, C>),
114
&*state as *const StartState as *mut _,
115
);
116
117
if fiber.is_null() {
118
drop(Box::from_raw(state.initial_closure.get().cast::<F>()));
119
return Err(io::Error::last_os_error());
120
}
121
122
Ok(Self { fiber, state })
123
}
124
}
125
126
pub(crate) fn resume<A, B, C>(&self, _stack: &FiberStack, result: &Cell<RunResult<A, B, C>>) {
127
unsafe {
128
let is_fiber = IsThreadAFiber() != 0;
129
let parent_fiber = if is_fiber {
130
wasmtime_fiber_get_current()
131
} else {
132
// Newer Rust versions use fiber local storage to register an internal hook that
133
// calls thread locals' destructors on thread exit.
134
// This has a limitation: the hook only runs in a regular thread (not in a fiber).
135
// We convert back into a thread once execution returns to this function,
136
// but we must also ensure that the hook is registered before converting into a fiber.
137
// Otherwise, a different fiber could be the first to register the hook,
138
// causing the hook to be called (and skipped) prematurely when that fiber is deleted.
139
struct Guard;
140
141
impl Drop for Guard {
142
fn drop(&mut self) {}
143
}
144
assert!(needs_drop::<Guard>());
145
thread_local!(static GUARD: Guard = Guard);
146
GUARD.with(|_g| {});
147
ConvertThreadToFiber(ptr::null_mut())
148
};
149
assert!(
150
!parent_fiber.is_null(),
151
"failed to make current thread a fiber"
152
);
153
self.state
154
.result_location
155
.set(result as *const _ as *const _);
156
self.state.parent.set(parent_fiber);
157
SwitchToFiber(self.fiber);
158
self.state.parent.set(ptr::null_mut());
159
self.state.result_location.set(ptr::null());
160
if !is_fiber {
161
let res = ConvertFiberToThread();
162
assert!(res != 0, "failed to convert main thread back");
163
}
164
}
165
}
166
167
pub(crate) unsafe fn drop<A, B, C>(&mut self) {}
168
}
169
170
impl Drop for Fiber {
171
fn drop(&mut self) {
172
unsafe {
173
DeleteFiber(self.fiber);
174
}
175
}
176
}
177
178
impl Suspend {
179
pub(crate) fn switch<A, B, C>(&self, result: RunResult<A, B, C>) -> A {
180
unsafe {
181
(*self.result_location::<A, B, C>()).set(result);
182
debug_assert!(IsThreadAFiber() != 0);
183
let parent = (*self.state).parent.get();
184
debug_assert!(!parent.is_null());
185
SwitchToFiber(parent);
186
self.take_resume::<A, B, C>()
187
}
188
}
189
190
pub(crate) fn exit<A, B, C>(&mut self, result: RunResult<A, B, C>) {
191
self.switch(result);
192
unreachable!()
193
}
194
195
unsafe fn take_resume<A, B, C>(&self) -> A {
196
unsafe {
197
match (*self.result_location::<A, B, C>()).replace(RunResult::Executing) {
198
RunResult::Resuming(val) => val,
199
_ => panic!("not in resuming state"),
200
}
201
}
202
}
203
204
unsafe fn result_location<A, B, C>(&self) -> *const Cell<RunResult<A, B, C>> {
205
unsafe {
206
let ret = (*self.state)
207
.result_location
208
.get()
209
.cast::<Cell<RunResult<A, B, C>>>();
210
assert!(!ret.is_null());
211
return ret;
212
}
213
}
214
}
215
216