Skip to content

Commit 59ddbed

Browse files
committed
Make core instance allocation an async function
This commit is a step in preparation for bytecodealliance#11430, notably core instance allocation, or `StoreOpaque::allocate_instance` is now an `async fn`. This function does not actually use the `async`-ness just yet so it's a noop from that point of view, but this propagates outwards to enough locations that I wanted to split this off to make future changes more digestable. Notably some creation functions here such as making an `Instance`, `Table`, or `Memory` are refactored internally to use this new `async` function. Annotations of `assert_ready` or `one_poll` are used as appropriate as well. For reference this commit was benchmarked with our `instantiation.rs` benchmark in the pooling allocator and shows no changes relative to the original baseline from before-`async`-PRs.
1 parent 623ceb8 commit 59ddbed

File tree

10 files changed

+99
-131
lines changed

10 files changed

+99
-131
lines changed

crates/wasmtime/src/runtime/component/instance.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ impl<'a> Instantiator<'a> {
634634
}
635635
}
636636

637-
fn run<T>(&mut self, store: &mut StoreContextMut<'_, T>) -> Result<()> {
637+
async fn run<T>(&mut self, store: &mut StoreContextMut<'_, T>) -> Result<()> {
638638
let env_component = self.component.env_component();
639639

640640
// Before all initializers are processed configure all destructors for
@@ -714,7 +714,7 @@ impl<'a> Instantiator<'a> {
714714
// if required.
715715

716716
let i = unsafe {
717-
crate::Instance::new_started_impl(store, module, imports.as_ref())?
717+
crate::Instance::new_started(store, module, imports.as_ref()).await?
718718
};
719719
self.instance_mut(store.0).push_instance_id(i.id());
720720
}
@@ -991,37 +991,26 @@ impl<T: 'static> InstancePre<T> {
991991
!store.as_context().async_support(),
992992
"must use async instantiation when async support is enabled"
993993
);
994-
self.instantiate_impl(store)
994+
vm::assert_ready(self._instantiate(store))
995995
}
996996
/// Performs the instantiation process into the store specified.
997997
///
998998
/// Exactly like [`Self::instantiate`] except for use on async stores.
999999
//
10001000
// TODO: needs more docs
10011001
#[cfg(feature = "async")]
1002-
pub async fn instantiate_async(
1003-
&self,
1004-
mut store: impl AsContextMut<Data = T>,
1005-
) -> Result<Instance>
1006-
where
1007-
T: Send,
1008-
{
1009-
let mut store = store.as_context_mut();
1010-
assert!(
1011-
store.0.async_support(),
1012-
"must use sync instantiation when async support is disabled"
1013-
);
1014-
store.on_fiber(|store| self.instantiate_impl(store)).await?
1002+
pub async fn instantiate_async(&self, store: impl AsContextMut<Data = T>) -> Result<Instance> {
1003+
self._instantiate(store).await
10151004
}
10161005

1017-
fn instantiate_impl(&self, mut store: impl AsContextMut<Data = T>) -> Result<Instance> {
1006+
async fn _instantiate(&self, mut store: impl AsContextMut<Data = T>) -> Result<Instance> {
10181007
let mut store = store.as_context_mut();
10191008
store
10201009
.engine()
10211010
.allocator()
10221011
.increment_component_instance_count()?;
10231012
let mut instantiator = Instantiator::new(&self.component, store.0, &self.imports);
1024-
instantiator.run(&mut store).map_err(|e| {
1013+
instantiator.run(&mut store).await.map_err(|e| {
10251014
store
10261015
.engine()
10271016
.allocator()

crates/wasmtime/src/runtime/externals/table.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ impl Table {
9494
/// # }
9595
/// ```
9696
pub fn new(mut store: impl AsContextMut, ty: TableType, init: Ref) -> Result<Table> {
97-
Table::_new(store.as_context_mut().0, ty, init)
97+
vm::one_poll(Table::_new(store.as_context_mut().0, ty, init))
98+
.expect("must use `new_async` when async resource limiters are in use")
9899
}
99100

100101
/// Async variant of [`Table::new`]. You must use this variant with
@@ -111,18 +112,11 @@ impl Table {
111112
ty: TableType,
112113
init: Ref,
113114
) -> Result<Table> {
114-
let mut store = store.as_context_mut();
115-
assert!(
116-
store.0.async_support(),
117-
"cannot use `new_async` without enabling async support on the config"
118-
);
119-
store
120-
.on_fiber(|store| Table::_new(store.0, ty, init))
121-
.await?
115+
Table::_new(store.as_context_mut().0, ty, init).await
122116
}
123117

124-
fn _new(store: &mut StoreOpaque, ty: TableType, init: Ref) -> Result<Table> {
125-
let table = generate_table_export(store, &ty)?;
118+
async fn _new(store: &mut StoreOpaque, ty: TableType, init: Ref) -> Result<Table> {
119+
let table = generate_table_export(store, &ty).await?;
126120
table._fill(store, 0, init, ty.minimum())?;
127121
Ok(table)
128122
}

crates/wasmtime/src/runtime/instance.rs

Lines changed: 30 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ impl Instance {
117117
// Note that the unsafety here should be satisfied by the call to
118118
// `typecheck_externs` above which satisfies the condition that all
119119
// the imports are valid for this module.
120-
unsafe { Instance::new_started(&mut store, module, imports.as_ref()) }
120+
assert!(!store.0.async_support());
121+
vm::assert_ready(unsafe { Instance::new_started(&mut store, module, imports.as_ref()) })
121122
}
122123

123124
/// Same as [`Instance::new`], except for usage in [asynchronous stores].
@@ -197,7 +198,7 @@ impl Instance {
197198
let mut store = store.as_context_mut();
198199
let imports = Instance::typecheck_externs(store.0, module, imports)?;
199200
// See `new` for notes on this unsafety
200-
unsafe { Instance::new_started_async(&mut store, module, imports.as_ref()).await }
201+
unsafe { Instance::new_started(&mut store, module, imports.as_ref()).await }
201202
}
202203

203204
fn typecheck_externs(
@@ -239,62 +240,31 @@ impl Instance {
239240
/// Internal function to create an instance and run the start function.
240241
///
241242
/// This function's unsafety is the same as `Instance::new_raw`.
242-
pub(crate) unsafe fn new_started<T>(
243-
store: &mut StoreContextMut<'_, T>,
244-
module: &Module,
245-
imports: Imports<'_>,
246-
) -> Result<Instance> {
247-
assert!(
248-
!store.0.async_support(),
249-
"must use async instantiation when async support is enabled",
250-
);
251-
252-
// SAFETY: the safety contract of `new_started_impl` is the same as this
253-
// function.
254-
unsafe { Self::new_started_impl(store, module, imports) }
255-
}
256-
257-
/// Internal function to create an instance and run the start function.
258-
///
259-
/// ONLY CALL THIS IF YOU HAVE ALREADY CHECKED FOR ASYNCNESS AND HANDLED
260-
/// THE FIBER NONSENSE
261-
pub(crate) unsafe fn new_started_impl<T>(
243+
pub(crate) async unsafe fn new_started<T>(
262244
store: &mut StoreContextMut<'_, T>,
263245
module: &Module,
264246
imports: Imports<'_>,
265247
) -> Result<Instance> {
266248
// SAFETY: the safety contract of `new_raw` is the same as this
267249
// function.
268-
let (instance, start) = unsafe { Instance::new_raw(store.0, module, imports)? };
250+
let (instance, start) = unsafe { Instance::new_raw(store.0, module, imports).await? };
269251
if let Some(start) = start {
270-
instance.start_raw(store, start)?;
252+
if store.0.async_support() {
253+
#[cfg(feature = "async")]
254+
{
255+
store
256+
.on_fiber(|store| instance.start_raw(store, start))
257+
.await??;
258+
}
259+
#[cfg(not(feature = "async"))]
260+
unreachable!();
261+
} else {
262+
instance.start_raw(store, start)?;
263+
}
271264
}
272265
Ok(instance)
273266
}
274267

275-
/// Internal function to create an instance and run the start function.
276-
///
277-
/// This function's unsafety is the same as `Instance::new_raw`.
278-
#[cfg(feature = "async")]
279-
async unsafe fn new_started_async<T>(
280-
store: &mut StoreContextMut<'_, T>,
281-
module: &Module,
282-
imports: Imports<'_>,
283-
) -> Result<Instance> {
284-
assert!(
285-
store.0.async_support(),
286-
"must use sync instantiation when async support is disabled",
287-
);
288-
289-
store
290-
.on_fiber(|store| {
291-
// SAFETY: the unsafe contract of `new_started_impl` is the same
292-
// as this function.
293-
unsafe { Self::new_started_impl(store, module, imports) }
294-
})
295-
.await?
296-
}
297-
298268
/// Internal function to create an instance which doesn't have its `start`
299269
/// function run yet.
300270
///
@@ -310,7 +280,7 @@ impl Instance {
310280
/// This method is unsafe because it does not type-check the `imports`
311281
/// provided. The `imports` provided must be suitable for the module
312282
/// provided as well.
313-
unsafe fn new_raw(
283+
async unsafe fn new_raw(
314284
store: &mut StoreOpaque,
315285
module: &Module,
316286
imports: Imports<'_>,
@@ -338,11 +308,13 @@ impl Instance {
338308
// SAFETY: this module, by construction, was already validated within
339309
// the store.
340310
let id = unsafe {
341-
store.allocate_instance(
342-
AllocateInstanceKind::Module(module_id),
343-
&ModuleRuntimeInfo::Module(module.clone()),
344-
imports,
345-
)?
311+
store
312+
.allocate_instance(
313+
AllocateInstanceKind::Module(module_id),
314+
&ModuleRuntimeInfo::Module(module.clone()),
315+
imports,
316+
)
317+
.await?
346318
};
347319

348320
// Additionally, before we start doing fallible instantiation, we
@@ -885,7 +857,10 @@ impl<T: 'static> InstancePre<T> {
885857
// This unsafety should be handled by the type-checking performed by the
886858
// constructor of `InstancePre` to assert that all the imports we're passing
887859
// in match the module we're instantiating.
888-
unsafe { Instance::new_started(&mut store, &self.module, imports.as_ref()) }
860+
assert!(!store.0.async_support());
861+
vm::assert_ready(unsafe {
862+
Instance::new_started(&mut store, &self.module, imports.as_ref())
863+
})
889864
}
890865

891866
/// Creates a new instance, running the start function asynchronously
@@ -919,7 +894,7 @@ impl<T: 'static> InstancePre<T> {
919894
// This unsafety should be handled by the type-checking performed by the
920895
// constructor of `InstancePre` to assert that all the imports we're passing
921896
// in match the module we're instantiating.
922-
unsafe { Instance::new_started_async(&mut store, &self.module, imports.as_ref()).await }
897+
unsafe { Instance::new_started(&mut store, &self.module, imports.as_ref()).await }
923898
}
924899
}
925900

crates/wasmtime/src/runtime/memory.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::Trap;
22
use crate::prelude::*;
3+
use crate::runtime::vm;
34
use crate::store::{StoreInstanceId, StoreOpaque};
45
use crate::trampoline::generate_memory_export;
56
use crate::{AsContext, AsContextMut, Engine, MemoryType, StoreContext, StoreContextMut};
@@ -259,7 +260,8 @@ impl Memory {
259260
/// # }
260261
/// ```
261262
pub fn new(mut store: impl AsContextMut, ty: MemoryType) -> Result<Memory> {
262-
Self::_new(store.as_context_mut().0, ty)
263+
vm::one_poll(Self::_new(store.as_context_mut().0, ty))
264+
.expect("must use `new_async` when async resource limiters are in use")
263265
}
264266

265267
/// Async variant of [`Memory::new`]. You must use this variant with
@@ -272,17 +274,12 @@ impl Memory {
272274
/// [`Store`](`crate::Store`).
273275
#[cfg(feature = "async")]
274276
pub async fn new_async(mut store: impl AsContextMut, ty: MemoryType) -> Result<Memory> {
275-
let mut store = store.as_context_mut();
276-
assert!(
277-
store.0.async_support(),
278-
"cannot use `new_async` without enabling async support on the config"
279-
);
280-
store.on_fiber(|store| Self::_new(store.0, ty)).await?
277+
Self::_new(store.as_context_mut().0, ty).await
281278
}
282279

283280
/// Helper function for attaching the memory to a "frankenstein" instance
284-
fn _new(store: &mut StoreOpaque, ty: MemoryType) -> Result<Memory> {
285-
generate_memory_export(store, &ty, None)
281+
async fn _new(store: &mut StoreOpaque, ty: MemoryType) -> Result<Memory> {
282+
generate_memory_export(store, &ty, None).await
286283
}
287284

288285
/// Returns the underlying type of this memory.
@@ -1007,7 +1004,10 @@ impl SharedMemory {
10071004
/// Construct a single-memory instance to provide a way to import
10081005
/// [`SharedMemory`] into other modules.
10091006
pub(crate) fn vmimport(&self, store: &mut StoreOpaque) -> crate::runtime::vm::VMMemoryImport {
1010-
generate_memory_export(store, &self.ty(), Some(&self.vm))
1007+
// Note `vm::assert_ready` shouldn't panic here because this isn't
1008+
// actually allocating any new memory so resource limiting shouldn't
1009+
// kick in.
1010+
vm::assert_ready(generate_memory_export(store, &self.ty(), Some(&self.vm)))
10111011
.unwrap()
10121012
.vmimport(store)
10131013
}

crates/wasmtime/src/runtime/store.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -666,15 +666,17 @@ impl<T> Store<T> {
666666
.unwrap();
667667

668668
unsafe {
669-
let id = inner
670-
.allocate_instance(
671-
AllocateInstanceKind::Dummy {
672-
allocator: &allocator,
673-
},
674-
&shim,
675-
Default::default(),
676-
)
677-
.expect("failed to allocate default callee");
669+
// Note that this dummy instance doesn't allocate tables or memories
670+
// so it won't have an async await point meaning that it should be
671+
// ok to assert the future is always ready.
672+
let id = vm::assert_ready(inner.allocate_instance(
673+
AllocateInstanceKind::Dummy {
674+
allocator: &allocator,
675+
},
676+
&shim,
677+
Default::default(),
678+
))
679+
.expect("failed to allocate default callee");
678680
let default_caller_vmctx = inner.instance(id).vmctx();
679681
inner.default_caller_vmctx = default_caller_vmctx.into();
680682
}
@@ -2173,7 +2175,7 @@ at https://bytecodealliance.org/security.
21732175
///
21742176
/// The `imports` provided must be correctly sized/typed for the module
21752177
/// being allocated.
2176-
pub(crate) unsafe fn allocate_instance(
2178+
pub(crate) async unsafe fn allocate_instance(
21772179
&mut self,
21782180
kind: AllocateInstanceKind<'_>,
21792181
runtime_info: &ModuleRuntimeInfo,

crates/wasmtime/src/runtime/trampoline.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@ use crate::store::StoreOpaque;
1919
use crate::{MemoryType, TableType, TagType};
2020
use wasmtime_environ::{MemoryIndex, TableIndex, TagIndex};
2121

22-
pub fn generate_memory_export(
22+
pub async fn generate_memory_export(
2323
store: &mut StoreOpaque,
2424
m: &MemoryType,
2525
preallocation: Option<&SharedMemory>,
2626
) -> Result<crate::Memory> {
2727
let id = store.id();
28-
let instance = create_memory(store, m, preallocation)?;
28+
let instance = create_memory(store, m, preallocation).await?;
2929
Ok(store
3030
.instance_mut(instance)
3131
.get_exported_memory(id, MemoryIndex::from_u32(0)))
3232
}
3333

34-
pub fn generate_table_export(store: &mut StoreOpaque, t: &TableType) -> Result<crate::Table> {
34+
pub async fn generate_table_export(store: &mut StoreOpaque, t: &TableType) -> Result<crate::Table> {
3535
let id = store.id();
36-
let instance = create_table(store, t)?;
36+
let instance = create_table(store, t).await?;
3737
Ok(store
3838
.instance_mut(instance)
3939
.get_exported_table(id, TableIndex::from_u32(0)))

crates/wasmtime/src/runtime/trampoline/memory.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use wasmtime_environ::{
2424
/// This separate instance is necessary because Wasm objects in Wasmtime must be
2525
/// attached to instances (versus the store, e.g.) and some objects exist
2626
/// outside: a host-provided memory import, shared memory.
27-
pub fn create_memory(
27+
pub async fn create_memory(
2828
store: &mut StoreOpaque,
2929
memory_ty: &MemoryType,
3030
preallocation: Option<&SharedMemory>,
@@ -52,13 +52,15 @@ pub fn create_memory(
5252
ondemand: OnDemandInstanceAllocator::default(),
5353
};
5454
unsafe {
55-
store.allocate_instance(
56-
AllocateInstanceKind::Dummy {
57-
allocator: &allocator,
58-
},
59-
&ModuleRuntimeInfo::bare(Arc::new(module)),
60-
Default::default(),
61-
)
55+
store
56+
.allocate_instance(
57+
AllocateInstanceKind::Dummy {
58+
allocator: &allocator,
59+
},
60+
&ModuleRuntimeInfo::bare(Arc::new(module)),
61+
Default::default(),
62+
)
63+
.await
6264
}
6365
}
6466

0 commit comments

Comments
 (0)