Implement allocating fiber stacks for an instance allocator.
This commit implements allocating fiber stacks in an instance allocator. The on-demand instance allocator doesn't support custom stacks, so the implementation will use the allocation from `wasmtime-fiber` for the fiber stacks. In the future, the pooling instance allocator will return custom stacks to use on Linux and macOS. On Windows, the native fiber implementation will always be used.
This commit is contained in:
@@ -51,6 +51,27 @@ impl<'a, Resume, Yield, Return> Fiber<'a, Resume, Yield, Return> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new fiber with existing stack space that will execute `func`.
|
||||
///
|
||||
/// This function returns a `Fiber` which, when resumed, will execute `func`
|
||||
/// to completion. When desired the `func` can suspend itself via
|
||||
/// `Fiber::suspend`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must properly allocate the stack space with a guard page and
|
||||
/// make the pages accessible for correct behavior.
|
||||
pub unsafe fn new_with_stack(
|
||||
top_of_stack: *mut u8,
|
||||
func: impl FnOnce(Resume, &Suspend<Resume, Yield, Return>) -> Return + 'a,
|
||||
) -> io::Result<Fiber<'a, Resume, Yield, Return>> {
|
||||
Ok(Fiber {
|
||||
inner: imp::Fiber::new_with_stack(top_of_stack, func),
|
||||
done: Cell::new(false),
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Resumes execution of this fiber.
|
||||
///
|
||||
/// This function will transfer execution to the fiber and resume from where
|
||||
|
||||
@@ -35,10 +35,10 @@ use std::io;
|
||||
use std::ptr;
|
||||
|
||||
pub struct Fiber {
|
||||
// Description of the mmap region we own. This should be abstracted
|
||||
// eventually so we aren't personally mmap-ing this region.
|
||||
mmap: *mut libc::c_void,
|
||||
mmap_len: usize,
|
||||
// The top of the stack; for stacks allocated by the fiber implementation itself,
|
||||
// the base address of the allocation will be `top_of_stack.sub(alloc_len.unwrap())`
|
||||
top_of_stack: *mut u8,
|
||||
alloc_len: Option<usize>,
|
||||
}
|
||||
|
||||
pub struct Suspend {
|
||||
@@ -66,21 +66,40 @@ where
|
||||
}
|
||||
|
||||
impl Fiber {
|
||||
pub fn new<F, A, B, C>(stack_size: usize, func: F) -> io::Result<Fiber>
|
||||
pub fn new<F, A, B, C>(stack_size: usize, func: F) -> io::Result<Self>
|
||||
where
|
||||
F: FnOnce(A, &super::Suspend<A, B, C>) -> C,
|
||||
{
|
||||
let fiber = Self::alloc_with_stack(stack_size)?;
|
||||
fiber.init(func);
|
||||
Ok(fiber)
|
||||
}
|
||||
|
||||
pub fn new_with_stack<F, A, B, C>(top_of_stack: *mut u8, func: F) -> Self
|
||||
where
|
||||
F: FnOnce(A, &super::Suspend<A, B, C>) -> C,
|
||||
{
|
||||
let fiber = Self {
|
||||
top_of_stack,
|
||||
alloc_len: None,
|
||||
};
|
||||
|
||||
fiber.init(func);
|
||||
|
||||
fiber
|
||||
}
|
||||
|
||||
fn init<F, A, B, C>(&self, func: F)
|
||||
where
|
||||
F: FnOnce(A, &super::Suspend<A, B, C>) -> C,
|
||||
{
|
||||
let fiber = Fiber::alloc_with_stack(stack_size)?;
|
||||
unsafe {
|
||||
// Initialize the top of the stack to be resumed from
|
||||
let top_of_stack = fiber.top_of_stack();
|
||||
let data = Box::into_raw(Box::new(func)).cast();
|
||||
wasmtime_fiber_init(top_of_stack, fiber_start::<F, A, B, C>, data);
|
||||
Ok(fiber)
|
||||
wasmtime_fiber_init(self.top_of_stack, fiber_start::<F, A, B, C>, data);
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_with_stack(stack_size: usize) -> io::Result<Fiber> {
|
||||
fn alloc_with_stack(stack_size: usize) -> io::Result<Self> {
|
||||
unsafe {
|
||||
// Round up our stack size request to the nearest multiple of the
|
||||
// page size.
|
||||
@@ -104,7 +123,10 @@ impl Fiber {
|
||||
if mmap == libc::MAP_FAILED {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
let ret = Fiber { mmap, mmap_len };
|
||||
let ret = Self {
|
||||
top_of_stack: mmap.cast::<u8>().add(mmap_len),
|
||||
alloc_len: Some(mmap_len),
|
||||
};
|
||||
let res = libc::mprotect(
|
||||
mmap.cast::<u8>().add(page_size).cast(),
|
||||
stack_size,
|
||||
@@ -124,27 +146,24 @@ impl Fiber {
|
||||
// stack, otherwise known as our reserved slot for this information.
|
||||
//
|
||||
// In the diagram above this is updating address 0xAff8
|
||||
let top_of_stack = self.top_of_stack();
|
||||
let addr = top_of_stack.cast::<usize>().offset(-1);
|
||||
let addr = self.top_of_stack.cast::<usize>().offset(-1);
|
||||
addr.write(result as *const _ as usize);
|
||||
|
||||
wasmtime_fiber_switch(top_of_stack);
|
||||
wasmtime_fiber_switch(self.top_of_stack);
|
||||
|
||||
// null this out to help catch use-after-free
|
||||
addr.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn top_of_stack(&self) -> *mut u8 {
|
||||
self.mmap.cast::<u8>().add(self.mmap_len)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Fiber {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let ret = libc::munmap(self.mmap, self.mmap_len);
|
||||
debug_assert!(ret == 0);
|
||||
if let Some(alloc_len) = self.alloc_len {
|
||||
let ret = libc::munmap(self.top_of_stack.sub(alloc_len) as _, alloc_len);
|
||||
debug_assert!(ret == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ where
|
||||
}
|
||||
|
||||
impl Fiber {
|
||||
pub fn new<F, A, B, C>(stack_size: usize, func: F) -> io::Result<Fiber>
|
||||
pub fn new<F, A, B, C>(stack_size: usize, func: F) -> io::Result<Self>
|
||||
where
|
||||
F: FnOnce(A, &super::Suspend<A, B, C>) -> C,
|
||||
{
|
||||
@@ -61,11 +61,19 @@ impl Fiber {
|
||||
drop(Box::from_raw(state.initial_closure.get().cast::<F>()));
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(Fiber { fiber, state })
|
||||
Ok(Self { fiber, state })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_stack<F, A, B, C>(_top_of_stack: *mut u8, _func: F) -> Self
|
||||
where
|
||||
F: FnOnce(A, &super::Suspend<A, B, C>) -> C,
|
||||
{
|
||||
// Windows fibers have no support for custom stacks
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub(crate) fn resume<A, B, C>(&self, result: &Cell<RunResult<A, B, C>>) {
|
||||
unsafe {
|
||||
let is_fiber = IsThreadAFiber() != 0;
|
||||
|
||||
Reference in New Issue
Block a user