Split out fiber stacks from fibers.
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 #2708.
This commit is contained in:
@@ -72,7 +72,7 @@ experimental_x64 = ["wasmtime-jit/experimental_x64"]
|
||||
|
||||
# Enables support for "async stores" as well as defining host functions as
|
||||
# `async fn` and calling functions asynchronously.
|
||||
async = ["wasmtime-fiber"]
|
||||
async = ["wasmtime-fiber", "wasmtime-runtime/async"]
|
||||
|
||||
# Enables userfaultfd support in the runtime's pooling allocator when building on Linux
|
||||
uffd = ["wasmtime-runtime/uffd"]
|
||||
|
||||
@@ -1332,25 +1332,20 @@ impl Config {
|
||||
match self.allocation_strategy {
|
||||
InstanceAllocationStrategy::OnDemand => Ok(Box::new(OnDemandInstanceAllocator::new(
|
||||
self.mem_creator.clone(),
|
||||
#[cfg(feature = "async")]
|
||||
self.async_stack_size,
|
||||
))),
|
||||
InstanceAllocationStrategy::Pooling {
|
||||
strategy,
|
||||
module_limits,
|
||||
instance_limits,
|
||||
} => {
|
||||
} => Ok(Box::new(PoolingInstanceAllocator::new(
|
||||
strategy.into(),
|
||||
module_limits.into(),
|
||||
instance_limits.into(),
|
||||
#[cfg(feature = "async")]
|
||||
let stack_size = self.async_stack_size;
|
||||
|
||||
#[cfg(not(feature = "async"))]
|
||||
let stack_size = 0;
|
||||
|
||||
Ok(Box::new(PoolingInstanceAllocator::new(
|
||||
strategy.into(),
|
||||
module_limits.into(),
|
||||
instance_limits.into(),
|
||||
stack_size,
|
||||
)?))
|
||||
}
|
||||
self.async_stack_size,
|
||||
)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ impl HostFunc {
|
||||
impl Drop for HostFunc {
|
||||
fn drop(&mut self) {
|
||||
// Host functions are always allocated with the default (on-demand) allocator
|
||||
unsafe { OnDemandInstanceAllocator::new(None).deallocate(&self.instance) }
|
||||
unsafe { OnDemandInstanceAllocator::default().deallocate(&self.instance) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ impl Store {
|
||||
pub fn get<T: Any>(&self) -> Option<&T> {
|
||||
let values = self.inner.context_values.borrow();
|
||||
|
||||
// Safety: a context value cannot be removed once added and therefore the addres is
|
||||
// Safety: a context value cannot be removed once added and therefore the address is
|
||||
// stable for the life of the store
|
||||
values
|
||||
.get(&TypeId::of::<T>())
|
||||
@@ -740,9 +740,15 @@ impl Store {
|
||||
debug_assert!(self.async_support());
|
||||
debug_assert!(config.async_stack_size > 0);
|
||||
|
||||
type SuspendType = wasmtime_fiber::Suspend<Result<(), Trap>, (), Result<(), Trap>>;
|
||||
let stack = self
|
||||
.inner
|
||||
.engine
|
||||
.allocator()
|
||||
.allocate_fiber_stack()
|
||||
.map_err(|e| Trap::from(anyhow::Error::from(e)))?;
|
||||
|
||||
let mut slot = None;
|
||||
let func = |keep_going, suspend: &SuspendType| {
|
||||
let fiber = wasmtime_fiber::Fiber::new(stack, |keep_going, suspend| {
|
||||
// First check and see if we were interrupted/dropped, and only
|
||||
// continue if we haven't been.
|
||||
keep_going?;
|
||||
@@ -760,46 +766,19 @@ impl Store {
|
||||
|
||||
slot = Some(func());
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let (fiber, stack) = match self.inner.engine.allocator().allocate_fiber_stack() {
|
||||
Ok(stack) => {
|
||||
// Use the returned stack and deallocate it when finished
|
||||
(
|
||||
unsafe {
|
||||
wasmtime_fiber::Fiber::new_with_stack(stack, func)
|
||||
.map_err(|e| Trap::from(anyhow::Error::from(e)))?
|
||||
},
|
||||
stack,
|
||||
)
|
||||
}
|
||||
Err(wasmtime_runtime::FiberStackError::NotSupported) => {
|
||||
// The allocator doesn't support custom fiber stacks for the current platform
|
||||
// Request that the fiber itself allocate the stack
|
||||
(
|
||||
wasmtime_fiber::Fiber::new(config.async_stack_size, func)
|
||||
.map_err(|e| Trap::from(anyhow::Error::from(e)))?,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
Err(e) => return Err(Trap::from(anyhow::Error::from(e))),
|
||||
};
|
||||
})
|
||||
.map_err(|e| Trap::from(anyhow::Error::from(e)))?;
|
||||
|
||||
// Once we have the fiber representing our synchronous computation, we
|
||||
// wrap that in a custom future implementation which does the
|
||||
// translation from the future protocol to our fiber API.
|
||||
FiberFuture {
|
||||
fiber,
|
||||
store: self,
|
||||
stack,
|
||||
}
|
||||
.await?;
|
||||
FiberFuture { fiber, store: self }.await?;
|
||||
|
||||
return Ok(slot.unwrap());
|
||||
|
||||
struct FiberFuture<'a> {
|
||||
fiber: wasmtime_fiber::Fiber<'a, Result<(), Trap>, (), Result<(), Trap>>,
|
||||
store: &'a Store,
|
||||
stack: *mut u8,
|
||||
}
|
||||
|
||||
impl Future for FiberFuture<'_> {
|
||||
@@ -807,7 +786,7 @@ impl Store {
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
// We need to carry over this `cx` into our fiber's runtime
|
||||
// for when it trys to poll sub-futures that are created. Doing
|
||||
// for when it tries to poll sub-futures that are created. Doing
|
||||
// this must be done unsafely, however, since `cx` is only alive
|
||||
// for this one singular function call. Here we do a `transmute`
|
||||
// to extend the lifetime of `Context` so it can be stored in
|
||||
@@ -864,13 +843,12 @@ impl Store {
|
||||
// callers that they shouldn't be doing that.
|
||||
debug_assert!(result.is_ok());
|
||||
}
|
||||
if !self.stack.is_null() {
|
||||
unsafe {
|
||||
self.store
|
||||
.engine()
|
||||
.allocator()
|
||||
.deallocate_fiber_stack(self.stack)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
self.store
|
||||
.engine()
|
||||
.allocator()
|
||||
.deallocate_fiber_stack(self.fiber.stack());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -999,7 +977,7 @@ impl fmt::Debug for Store {
|
||||
impl Drop for StoreInner {
|
||||
fn drop(&mut self) {
|
||||
let allocator = self.engine.allocator();
|
||||
let ondemand = OnDemandInstanceAllocator::new(self.engine.config().mem_creator.clone());
|
||||
let ondemand = OnDemandInstanceAllocator::default();
|
||||
for instance in self.instances.borrow().iter() {
|
||||
unsafe {
|
||||
if instance.ondemand {
|
||||
|
||||
@@ -62,22 +62,27 @@ fn create_handle(
|
||||
imports.functions = func_imports;
|
||||
|
||||
unsafe {
|
||||
let config = store.engine().config();
|
||||
// Use the on-demand allocator when creating handles associated with host objects
|
||||
// The configured instance allocator should only be used when creating module instances
|
||||
// as we don't want host objects to count towards instance limits.
|
||||
let handle = OnDemandInstanceAllocator::new(store.engine().config().mem_creator.clone())
|
||||
.allocate(InstanceAllocationRequest {
|
||||
module: Arc::new(module),
|
||||
finished_functions: &finished_functions,
|
||||
imports,
|
||||
lookup_shared_signature: &|_| shared_signature_id.unwrap(),
|
||||
host_state,
|
||||
interrupts: store.interrupts(),
|
||||
externref_activations_table: store.externref_activations_table()
|
||||
as *const VMExternRefActivationsTable
|
||||
as *mut _,
|
||||
stack_map_registry: store.stack_map_registry() as *const StackMapRegistry as *mut _,
|
||||
})?;
|
||||
let handle = OnDemandInstanceAllocator::new(
|
||||
config.mem_creator.clone(),
|
||||
#[cfg(feature = "async")]
|
||||
config.async_stack_size,
|
||||
)
|
||||
.allocate(InstanceAllocationRequest {
|
||||
module: Arc::new(module),
|
||||
finished_functions: &finished_functions,
|
||||
imports,
|
||||
lookup_shared_signature: &|_| shared_signature_id.unwrap(),
|
||||
host_state,
|
||||
interrupts: store.interrupts(),
|
||||
externref_activations_table: store.externref_activations_table()
|
||||
as *const VMExternRefActivationsTable
|
||||
as *mut _,
|
||||
stack_map_registry: store.stack_map_registry() as *const StackMapRegistry as *mut _,
|
||||
})?;
|
||||
|
||||
Ok(store.add_instance(handle, true))
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ pub fn create_function(
|
||||
|
||||
unsafe {
|
||||
Ok((
|
||||
OnDemandInstanceAllocator::new(None).allocate(InstanceAllocationRequest {
|
||||
OnDemandInstanceAllocator::default().allocate(InstanceAllocationRequest {
|
||||
module: Arc::new(module),
|
||||
finished_functions: &finished_functions,
|
||||
imports: Imports::default(),
|
||||
@@ -308,7 +308,7 @@ pub unsafe fn create_raw_function(
|
||||
finished_functions.push(func);
|
||||
|
||||
Ok(
|
||||
OnDemandInstanceAllocator::new(None).allocate(InstanceAllocationRequest {
|
||||
OnDemandInstanceAllocator::default().allocate(InstanceAllocationRequest {
|
||||
module: Arc::new(module),
|
||||
finished_functions: &finished_functions,
|
||||
imports: Imports::default(),
|
||||
|
||||
Reference in New Issue
Block a user