Skip to content

Commit 91bf501

Browse files
authored
Refactor defining core wasm async functions (#12365)
* Refactor defining core wasm async functions There was some preexisting duplication between `Func`-related constructors and `Linker`-related constructors. This consolidates everything into `HostFunc` which duplicates type signatures a bit more but complexity-wise reduces the amount of internal duplication. This additionally notably cuts down on usage of `block_on` literally, if not conceptually, which is going to eventually be important for rationalizing it with `*_concurrent` functions. * Adjust some features
1 parent 8e194be commit 91bf501

File tree

3 files changed

+115
-129
lines changed

3 files changed

+115
-129
lines changed

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

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -771,9 +771,9 @@ impl<T: 'static> LinkerInstance<'_, T> {
771771
ty: ResourceType,
772772
dtor: impl Fn(StoreContextMut<'_, T>, u32) -> Result<()> + Send + Sync + 'static,
773773
) -> Result<()> {
774-
let dtor = Arc::new(crate::func::HostFunc::wrap_inner(
774+
let dtor = Arc::new(crate::func::HostFunc::wrap(
775775
&self.engine,
776-
move |mut cx: crate::Caller<'_, T>, (param,): (u32,)| dtor(cx.as_context_mut(), param),
776+
move |mut cx: crate::Caller<'_, T>, param: u32| dtor(cx.as_context_mut(), param),
777777
));
778778
self.insert(name, Definition::Resource(ty, dtor))?;
779779
Ok(())
@@ -792,12 +792,9 @@ impl<T: 'static> LinkerInstance<'_, T> {
792792
self.engine.config().async_support,
793793
"cannot use `resource_async` without enabling async support in the config"
794794
);
795-
let dtor = Arc::new(crate::func::HostFunc::wrap_inner(
795+
let dtor = Arc::new(crate::func::HostFunc::wrap_async(
796796
&self.engine,
797-
move |mut cx: crate::Caller<'_, T>, (param,): (u32,)| {
798-
cx.as_context_mut()
799-
.block_on(|store| dtor(store, param).into())?
800-
},
797+
move |cx: crate::Caller<'_, T>, (param,): (u32,)| dtor(cx.into(), param),
801798
));
802799
self.insert(name, Definition::Resource(ty, dtor))?;
803800
Ok(())
@@ -823,23 +820,20 @@ impl<T: 'static> LinkerInstance<'_, T> {
823820
// up the implementation to avoid using e.g. `Accessor::new` and
824821
// `tls::set` directly.
825822
let dtor = Arc::new(dtor);
826-
let dtor = Arc::new(crate::func::HostFunc::wrap_inner(
823+
let dtor = Arc::new(crate::func::HostFunc::wrap_async(
827824
&self.engine,
828825
move |mut cx: crate::Caller<'_, T>, (param,): (u32,)| {
829826
let dtor = dtor.clone();
830-
cx.as_context_mut().block_on(move |mut store| {
831-
Box::pin(async move {
832-
let accessor =
833-
&Accessor::new(crate::store::StoreToken::new(store.as_context_mut()));
834-
let mut future = std::pin::pin!(dtor(accessor, param));
835-
std::future::poll_fn(|cx| {
836-
crate::component::concurrent::tls::set(store.0, || {
837-
future.as_mut().poll(cx)
838-
})
839-
})
840-
.await
827+
Box::new(async move {
828+
let mut store = cx.as_context_mut();
829+
let accessor =
830+
&Accessor::new(crate::store::StoreToken::new(store.as_context_mut()));
831+
let mut future = std::pin::pin!(dtor(accessor, param));
832+
std::future::poll_fn(|cx| {
833+
crate::component::concurrent::tls::set(store.0, || future.as_mut().poll(cx))
841834
})
842-
})?
835+
.await
836+
})
843837
},
844838
));
845839
self.insert(name, Definition::Resource(ty, dtor))?;

crates/wasmtime/src/runtime/func.rs

Lines changed: 87 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -371,17 +371,16 @@ impl Func {
371371
/// Panics if the given function type is not associated with this store's
372372
/// engine.
373373
pub fn new<T: 'static>(
374-
store: impl AsContextMut<Data = T>,
374+
mut store: impl AsContextMut<Data = T>,
375375
ty: FuncType,
376376
func: impl Fn(Caller<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static,
377377
) -> Self {
378-
assert!(ty.comes_from_same_engine(store.as_context().engine()));
379-
let ty_clone = ty.clone();
380-
unsafe {
381-
Func::new_unchecked(store, ty, move |caller, values| {
382-
Func::invoke_host_func_for_wasm(caller, &ty_clone, values, &func)
383-
})
384-
}
378+
let store = store.as_context_mut().0;
379+
let host = HostFunc::new(store.engine(), ty, func);
380+
381+
// SAFETY: the `T` used by `func` matches the `T` of the store we're
382+
// inserting into via this function's type signature.
383+
unsafe { host.into_func(store) }
385384
}
386385

387386
/// Creates a new [`Func`] with the given arguments, although has fewer
@@ -417,7 +416,6 @@ impl Func {
417416
ty: FuncType,
418417
func: impl Fn(Caller<'_, T>, &mut [ValRaw]) -> Result<()> + Send + Sync + 'static,
419418
) -> Self {
420-
assert!(ty.comes_from_same_engine(store.as_context().engine()));
421419
let store = store.as_context_mut().0;
422420

423421
// SAFETY: the contract required by `new_unchecked` is the same as the
@@ -502,7 +500,7 @@ impl Func {
502500
/// # }
503501
/// ```
504502
#[cfg(feature = "async")]
505-
pub fn new_async<T, F>(store: impl AsContextMut<Data = T>, ty: FuncType, func: F) -> Func
503+
pub fn new_async<T, F>(mut store: impl AsContextMut<Data = T>, ty: FuncType, func: F) -> Func
506504
where
507505
F: for<'a> Fn(
508506
Caller<'a, T>,
@@ -514,24 +512,16 @@ impl Func {
514512
+ 'static,
515513
T: 'static,
516514
{
515+
let store = store.as_context_mut().0;
517516
assert!(
518-
store.as_context().async_support(),
517+
store.async_support(),
519518
"cannot use `new_async` without enabling async support in the config"
520519
);
521-
assert!(ty.comes_from_same_engine(store.as_context().engine()));
522-
return Func::new(
523-
store,
524-
ty,
525-
move |Caller { store, caller }, params, results| {
526-
store.with_blocking(|store, cx| {
527-
cx.block_on(core::pin::Pin::from(func(
528-
Caller { store, caller },
529-
params,
530-
results,
531-
)))
532-
})?
533-
},
534-
);
520+
let host = HostFunc::new_async(store.engine(), ty, func);
521+
522+
// SAFETY: the `T` used by `func` matches the `T` of the store we're
523+
// inserting into via this function's type signature.
524+
unsafe { host.into_func(store) }
535525
}
536526

537527
/// Creates a new `Func` from a store and a funcref within that store.
@@ -812,22 +802,6 @@ impl Func {
812802
unsafe { host.into_func(store) }
813803
}
814804

815-
#[cfg(feature = "async")]
816-
fn wrap_inner<F, T, Params, Results>(mut store: impl AsContextMut<Data = T>, func: F) -> Func
817-
where
818-
F: Fn(Caller<'_, T>, Params) -> Results + Send + Sync + 'static,
819-
Params: WasmTyList,
820-
Results: WasmRet,
821-
T: 'static,
822-
{
823-
let store = store.as_context_mut().0;
824-
let host = HostFunc::wrap_inner(store.engine(), func);
825-
826-
// SAFETY: The `T` the closure takes is the same as the `T` of the store
827-
// we're inserting into via the type signature above.
828-
unsafe { host.into_func(store) }
829-
}
830-
831805
/// Same as [`Func::wrap`], except the closure asynchronously produces the
832806
/// result and the arguments are passed within a tuple. For more information
833807
/// see the [`Func`] documentation.
@@ -836,7 +810,7 @@ impl Func {
836810
///
837811
/// This function will panic if called with a non-asynchronous store.
838812
#[cfg(feature = "async")]
839-
pub fn wrap_async<T, F, P, R>(store: impl AsContextMut<Data = T>, func: F) -> Func
813+
pub fn wrap_async<T, F, P, R>(mut store: impl AsContextMut<Data = T>, func: F) -> Func
840814
where
841815
F: for<'a> Fn(Caller<'a, T>, P) -> Box<dyn Future<Output = R> + Send + 'a>
842816
+ Send
@@ -846,16 +820,16 @@ impl Func {
846820
R: WasmRet,
847821
T: 'static,
848822
{
823+
let store = store.as_context_mut().0;
849824
assert!(
850-
store.as_context().async_support(),
825+
store.async_support(),
851826
concat!("cannot use `wrap_async` without enabling async support on the config")
852827
);
853-
Func::wrap_inner(store, move |Caller { store, caller }, args| {
854-
match store.block_on(|store| func(Caller { store, caller }, args).into()) {
855-
Ok(ret) => ret.into_fallible(),
856-
Err(e) => R::fallible_from_error(e),
857-
}
858-
})
828+
let host = HostFunc::wrap_async(store.engine(), func);
829+
830+
// SAFETY: The `T` the closure takes is the same as the `T` of the store
831+
// we're inserting into via the type signature above.
832+
unsafe { host.into_func(store) }
859833
}
860834

861835
/// Returns the underlying wasm type that this `Func` has.
@@ -1949,7 +1923,7 @@ macro_rules! impl_into_func {
19491923
for_each_function_signature!(impl_into_func);
19501924

19511925
/// Trait implemented for various tuples made up of types which implement
1952-
/// [`WasmTy`] that can be passed to [`Func::wrap_inner`] and
1926+
/// [`WasmTy`] that can be passed to [`Func::wrap_async`] and
19531927
/// [`HostContext::from_closure`].
19541928
pub unsafe trait WasmTyList {
19551929
/// Get the value type that each Type in the list represents.
@@ -2028,16 +2002,6 @@ pub struct Caller<'a, T: 'static> {
20282002
}
20292003

20302004
impl<T> Caller<'_, T> {
2031-
#[cfg(feature = "async")]
2032-
pub(crate) fn new(store: StoreContextMut<'_, T>, caller: Instance) -> Caller<'_, T> {
2033-
Caller { store, caller }
2034-
}
2035-
2036-
#[cfg(feature = "async")]
2037-
pub(crate) fn caller(&self) -> Instance {
2038-
self.caller
2039-
}
2040-
20412005
/// Executes `f` with an appropriate `Caller`.
20422006
///
20432007
/// This is the entrypoint for host functions in core wasm. The `store` and
@@ -2257,6 +2221,12 @@ impl<T: 'static> AsContextMut for Caller<'_, T> {
22572221
}
22582222
}
22592223

2224+
impl<'a, T: 'static> From<Caller<'a, T>> for StoreContextMut<'a, T> {
2225+
fn from(caller: Caller<'a, T>) -> Self {
2226+
caller.store
2227+
}
2228+
}
2229+
22602230
// State stored inside a `VMArrayCallHostFuncContext`.
22612231
struct HostFuncState<F> {
22622232
// The actual host function.
@@ -2457,6 +2427,40 @@ impl HostFunc {
24572427
}
24582428
}
24592429

2430+
/// Analog of [`Func::new_async`]
2431+
///
2432+
/// # Panics
2433+
///
2434+
/// Panics if the given function type is not associated with the given
2435+
/// engine.
2436+
#[cfg(feature = "async")]
2437+
pub fn new_async<T, F>(engine: &Engine, ty: FuncType, func: F) -> Self
2438+
where
2439+
F: for<'a> Fn(
2440+
Caller<'a, T>,
2441+
&'a [Val],
2442+
&'a mut [Val],
2443+
) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
2444+
+ Send
2445+
+ Sync
2446+
+ 'static,
2447+
T: 'static,
2448+
{
2449+
HostFunc::new(
2450+
engine,
2451+
ty,
2452+
move |Caller { store, caller }, params, results| {
2453+
store.with_blocking(|store, cx| {
2454+
cx.block_on(core::pin::Pin::from(func(
2455+
Caller { store, caller },
2456+
params,
2457+
results,
2458+
)))
2459+
})?
2460+
},
2461+
)
2462+
}
2463+
24602464
/// Analog of [`Func::new_unchecked`]
24612465
///
24622466
/// # Panics
@@ -2498,19 +2502,6 @@ impl HostFunc {
24982502
HostFunc::_new(engine, ctx.into())
24992503
}
25002504

2501-
/// Analog of [`Func::wrap_inner`]
2502-
#[cfg(any(feature = "component-model", feature = "async"))]
2503-
pub fn wrap_inner<F, T, Params, Results>(engine: &Engine, func: F) -> Self
2504-
where
2505-
F: Fn(Caller<'_, T>, Params) -> Results + Send + Sync + 'static,
2506-
Params: WasmTyList,
2507-
Results: WasmRet,
2508-
T: 'static,
2509-
{
2510-
let ctx = HostContext::from_closure(engine, func);
2511-
HostFunc::_new(engine, ctx)
2512-
}
2513-
25142505
/// Analog of [`Func::wrap`]
25152506
pub fn wrap<T, Params, Results>(
25162507
engine: &Engine,
@@ -2523,6 +2514,29 @@ impl HostFunc {
25232514
HostFunc::_new(engine, ctx)
25242515
}
25252516

2517+
/// Analog of [`Func::wrap_async`]
2518+
#[cfg(feature = "async")]
2519+
pub fn wrap_async<T, F, P, R>(engine: &Engine, func: F) -> Self
2520+
where
2521+
F: for<'a> Fn(Caller<'a, T>, P) -> Box<dyn Future<Output = R> + Send + 'a>
2522+
+ Send
2523+
+ Sync
2524+
+ 'static,
2525+
P: WasmTyList,
2526+
R: WasmRet,
2527+
T: 'static,
2528+
{
2529+
HostFunc::_new(
2530+
engine,
2531+
HostContext::from_closure(engine, move |Caller { store, caller }, args| {
2532+
match store.block_on(|store| func(Caller { store, caller }, args).into()) {
2533+
Ok(ret) => ret.into_fallible(),
2534+
Err(e) => R::fallible_from_error(e),
2535+
}
2536+
}),
2537+
)
2538+
}
2539+
25262540
/// Requires that this function's signature is already registered within
25272541
/// `Engine`. This happens automatically during the above two constructors.
25282542
fn _new(engine: &Engine, ctx: HostContext) -> Self {

0 commit comments

Comments
 (0)