Skip to content

Commit

Permalink
Split out fiber stacks from fibers.
Browse files Browse the repository at this point in the history
This commit splits out a `FiberStack` from `Fiber`, allowing the instance
allocator trait to return `FiberStack` rather than raw stack pointers. This
keeps the stack creation mostly in `wasmtime_fiber`, but now the on-demand
instance allocator can make use of it.

The instance allocators no longer have to return a "not supported" error to
indicate that the store should allocate its own fiber stack.

This includes a bunch of cleanup in the instance allocator to scope stacks to
the new "async" feature in the runtime.

Closes bytecodealliance#2708.
  • Loading branch information
peterhuene committed Mar 19, 2021
1 parent 59dfe4b commit f8f51af
Show file tree
Hide file tree
Showing 20 changed files with 342 additions and 291 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/fiber/src/arch/x86_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ FUNCTION(wasmtime_fiber_init):

// And then we specify the stack pointer resumption should begin at. Our
// `wasmtime_fiber_switch` function consumes 6 registers plus a return
// pointer, and the top 16 bytes aree resereved, so that's:
// pointer, and the top 16 bytes are reserved, so that's:
//
// (6 + 1) * 16 + 16 = 0x48
lea -0x48(%rdi), %rax
Expand Down
111 changes: 65 additions & 46 deletions crates/fiber/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,38 @@ mod unix;
#[cfg(unix)]
use unix as imp;

/// Represents an execution stack to use for a fiber.
#[derive(Debug)]
pub struct FiberStack(imp::FiberStack);

impl FiberStack {
/// Creates a new fiber stack of the given size.
pub fn new(size: usize) -> io::Result<Self> {
Ok(Self(imp::FiberStack::new(size)?))
}

/// Creates a new fiber stack with the given pointer to the top of the stack.
///
/// # Safety
///
/// This is unsafe because there is no validation of the given stack pointer.
///
/// The caller must properly allocate the stack space with a guard page and
/// make the pages accessible for correct behavior.
pub unsafe fn from_top_ptr(top: *mut u8) -> io::Result<Self> {
Ok(Self(imp::FiberStack::from_top_ptr(top)?))
}

/// Gets the top of the stack.
///
/// Returns `None` if the platform does not support getting the top of the stack.
pub fn top(&self) -> Option<*mut u8> {
self.0.top()
}
}

pub struct Fiber<'a, Resume, Yield, Return> {
stack: FiberStack,
inner: imp::Fiber,
done: Cell<bool>,
_phantom: PhantomData<&'a (Resume, Yield, Return)>,
Expand All @@ -34,39 +65,20 @@ enum RunResult<Resume, Yield, Return> {
}

impl<'a, Resume, Yield, Return> Fiber<'a, Resume, Yield, Return> {
/// Creates a new fiber which will execute `func` on a new native stack of
/// size `stack_size`.
/// Creates a new fiber which will execute `func` on the given stack.
///
/// This function returns a `Fiber` which, when resumed, will execute `func`
/// to completion. When desired the `func` can suspend itself via
/// `Fiber::suspend`.
pub fn new(
stack_size: usize,
stack: FiberStack,
func: impl FnOnce(Resume, &Suspend<Resume, Yield, Return>) -> Return + 'a,
) -> io::Result<Fiber<'a, Resume, Yield, Return>> {
Ok(Fiber {
inner: imp::Fiber::new(stack_size, func)?,
done: Cell::new(false),
_phantom: PhantomData,
})
}
) -> io::Result<Self> {
let inner = imp::Fiber::new(&stack.0, func)?;

/// 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)?,
Ok(Self {
stack,
inner,
done: Cell::new(false),
_phantom: PhantomData,
})
Expand All @@ -90,7 +102,7 @@ impl<'a, Resume, Yield, Return> Fiber<'a, Resume, Yield, Return> {
pub fn resume(&self, val: Resume) -> Result<Return, Yield> {
assert!(!self.done.replace(true), "cannot resume a finished fiber");
let result = Cell::new(RunResult::Resuming(val));
self.inner.resume(&result);
self.inner.resume(&self.stack.0, &result);
match result.into_inner() {
RunResult::Resuming(_) | RunResult::Executing => unreachable!(),
RunResult::Yield(y) => {
Expand All @@ -106,6 +118,11 @@ impl<'a, Resume, Yield, Return> Fiber<'a, Resume, Yield, Return> {
pub fn done(&self) -> bool {
self.done.get()
}

/// Gets the stack associated with this fiber.
pub fn stack(&self) -> &FiberStack {
&self.stack
}
}

impl<Resume, Yield, Return> Suspend<Resume, Yield, Return> {
Expand Down Expand Up @@ -148,18 +165,18 @@ impl<A, B, C> Drop for Fiber<'_, A, B, C> {

#[cfg(test)]
mod tests {
use super::Fiber;
use super::{Fiber, FiberStack};
use std::cell::Cell;
use std::panic::{self, AssertUnwindSafe};
use std::rc::Rc;

#[test]
fn small_stacks() {
Fiber::<(), (), ()>::new(0, |_, _| {})
Fiber::<(), (), ()>::new(FiberStack::new(0).unwrap(), |_, _| {})
.unwrap()
.resume(())
.unwrap();
Fiber::<(), (), ()>::new(1, |_, _| {})
Fiber::<(), (), ()>::new(FiberStack::new(1).unwrap(), |_, _| {})
.unwrap()
.resume(())
.unwrap();
Expand All @@ -169,7 +186,7 @@ mod tests {
fn smoke() {
let hit = Rc::new(Cell::new(false));
let hit2 = hit.clone();
let fiber = Fiber::<(), (), ()>::new(1024 * 1024, move |_, _| {
let fiber = Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024).unwrap(), move |_, _| {
hit2.set(true);
})
.unwrap();
Expand All @@ -182,7 +199,7 @@ mod tests {
fn suspend_and_resume() {
let hit = Rc::new(Cell::new(false));
let hit2 = hit.clone();
let fiber = Fiber::<(), (), ()>::new(1024 * 1024, move |_, s| {
let fiber = Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024).unwrap(), move |_, s| {
s.suspend(());
hit2.set(true);
s.suspend(());
Expand Down Expand Up @@ -219,14 +236,15 @@ mod tests {
}

fn run_test() {
let fiber = Fiber::<(), (), ()>::new(1024 * 1024, move |(), s| {
assert_contains_host();
s.suspend(());
assert_contains_host();
s.suspend(());
assert_contains_host();
})
.unwrap();
let fiber =
Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024).unwrap(), move |(), s| {
assert_contains_host();
s.suspend(());
assert_contains_host();
s.suspend(());
assert_contains_host();
})
.unwrap();
assert!(fiber.resume(()).is_err());
assert!(fiber.resume(()).is_err());
assert!(fiber.resume(()).is_ok());
Expand All @@ -239,11 +257,12 @@ mod tests {
fn panics_propagated() {
let a = Rc::new(Cell::new(false));
let b = SetOnDrop(a.clone());
let fiber = Fiber::<(), (), ()>::new(1024 * 1024, move |(), _s| {
drop(&b);
panic!();
})
.unwrap();
let fiber =
Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024).unwrap(), move |(), _s| {
drop(&b);
panic!();
})
.unwrap();
assert!(panic::catch_unwind(AssertUnwindSafe(|| fiber.resume(()))).is_err());
assert!(a.get());

Expand All @@ -258,7 +277,7 @@ mod tests {

#[test]
fn suspend_and_resume_values() {
let fiber = Fiber::new(1024 * 1024, move |first, s| {
let fiber = Fiber::new(FiberStack::new(1024 * 1024).unwrap(), move |first, s| {
assert_eq!(first, 2.0);
assert_eq!(s.suspend(4), 3.0);
"hello".to_string()
Expand Down
Loading

0 comments on commit f8f51af

Please sign in to comment.