-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Open
Labels
bugIncorrect behavior in the current implementation that needs fixingIncorrect behavior in the current implementation that needs fixingwasm-proposal:component-model-asyncIssues related to the WebAssembly Component Model async proposalIssues related to the WebAssembly Component Model async proposal
Description
Hi there! I want to link wasm components at runtime. How can I do this with version 39.0.0?
Test Case
Here is the test code:
use std::{collections::HashMap, sync::Arc};
use wasmtime::{
Config,
component::{Linker, types::ComponentItem},
};
use wasmtime_wasi::p2::bindings::CommandPre;
const SERVICE: &[u8] = include_bytes!("../cron-service.wasm");
const COMPONENT: &[u8] = include_bytes!("../cron_component.wasm");
struct Ctx {
id: String,
ctx: wasmtime_wasi::WasiCtx,
table: wasmtime_wasi::ResourceTable,
}
impl wasmtime_wasi::WasiView for Ctx {
fn ctx(&mut self) -> wasmtime_wasi::WasiCtxView<'_> {
wasmtime_wasi::WasiCtxView {
ctx: &mut self.ctx,
table: &mut self.table,
}
}
}
pub struct Component {
component: wasmtime::component::Component,
linker: Linker<Ctx>,
}
impl Component {
pub fn new(engine: wasmtime::Engine, bytes: &[u8]) -> Self {
let component = wasmtime::component::Component::new(&engine, bytes).unwrap();
let mut linker: Linker<Ctx> = wasmtime::component::Linker::new(&engine);
wasmtime_wasi::p2::add_to_linker_async(&mut linker).unwrap();
Self { component, linker }
}
}
#[tokio::main]
async fn main() {
let engine = wasmtime::Engine::new(
&Config::new()
.async_support(true)
.wasm_component_model_async(true),
)
.unwrap();
let mut service = Component::new(engine.clone(), SERVICE);
let mut component = Component::new(engine.clone(), COMPONENT);
resolve_dependencies(&engine, &mut service, &[&mut component]).await;
let pre = service.linker.instantiate_pre(&service.component).unwrap();
let cmd_pre = CommandPre::new(pre).unwrap();
let mut store = wasmtime::Store::new(
&engine,
Ctx {
id: "TEST".to_string(),
ctx: wasmtime_wasi::WasiCtx::builder().build(),
table: wasmtime_wasi::ResourceTable::new(),
},
);
let cmd = cmd_pre.instantiate_async(&mut store).await.unwrap();
cmd.wasi_cli_run()
.call_run(&mut store)
.await
.unwrap()
.unwrap();
}
async fn resolve_dependencies(
engine: &wasmtime::Engine,
component: &mut Component,
others: &[&mut Component],
) {
let mut exported_interfaces = HashMap::new();
for (idx, other) in others.iter().enumerate() {
for (export_name, export_item) in other.component.component_type().exports(&engine) {
if matches!(export_item, ComponentItem::ComponentInstance(_)) {
exported_interfaces.insert(export_name.to_string(), idx);
}
}
}
let instance_cache: Arc<tokio::sync::RwLock<Option<(String, wasmtime::component::Instance)>>> =
Arc::default();
for (import_name, import_item) in component.component.component_type().imports(engine) {
if let ComponentItem::ComponentInstance(import_instance) = import_item
&& let Some(exporter_idx) = exported_interfaces.get(import_name).copied()
{
let exporter = &others[exporter_idx];
let (_, export_instance_idx) =
exporter.component.get_export(None, import_name).unwrap();
let mut linker_instance = component.linker.instance(import_name).unwrap();
let pre = exporter
.linker
.instantiate_pre(&exporter.component)
.unwrap();
for (import_export_name, import_export_ty) in import_instance.exports(&engine) {
if let ComponentItem::ComponentFunc(_) = import_export_ty {
let (_, exported_func_idx) = exporter
.component
.get_export(Some(&export_instance_idx), import_export_name)
.unwrap();
let pre = pre.clone();
let instance_cache = instance_cache.clone();
linker_instance
.func_new_async(import_export_name, move |mut store, _, params, results| {
let pre = pre.clone();
let instance_cache = instance_cache.clone();
let id = store.data().id.clone();
Box::new(async move {
if let Some((store_id, instance)) =
instance_cache.read().await.clone()
{
if store_id == id {
let func = instance
.get_func(&mut store, exported_func_idx)
.unwrap();
func.call_async(&mut store, params, results).await?;
func.post_return_async(&mut store).await?;
return Ok(());
}
}
let new_instance = pre.instantiate_async(&mut store).await?;
*instance_cache.write().await = Some((id, new_instance.clone()));
let func = new_instance
.get_func(&mut store, exported_func_idx)
.unwrap();
func.call_async(&mut store, params, results).await.unwrap();
func.post_return_async(&mut store).await.unwrap();
Ok(())
})
})
.unwrap();
}
}
}
}
}Component code:
wit_bindgen::generate!({
world: "component",
async: true,
});
struct Component;
impl exports::wasmcloud::example::cron::Guest for Component {
async fn invoke() -> Result<(), String> {
eprintln!("Hello from the cron-component!");
Ok(())
}
}
export!(Component);Service code:
wit_bindgen::generate!({
world: "service",
async: true,
});
#[tokio::main(flavor = "current_thread")]
async fn main() {
eprintln!("Starting cron-service with 1 second intervals...");
loop {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
let _ = wasmcloud::example::cron::invoke().await;
}
}Shared wit file:
package wasmcloud:example@0.0.1;
interface cron {
invoke: func() -> result<_, string>;
}
world service {
import cron;
}
world component {
export cron;
}Steps to Reproduce
Expected Results
No panic
Actual Results
Panic:
cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
Running `target/debug/test_wt`
thread 'main' panicked at /home/pagafonov/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasmtime-39.0.1/src/runtime/component/concurrent.rs:4913:5:
assertion failed: state.guest_thread.is_none()
note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceVersions and Environment
tokio = { version = "1.48.0", features = ["full"] }
wasmtime = { version = "39.0.1" }
wasmtime-wasi = "39.0.1"
wit-bindgen = { version = "0.46.0", features = ["async"] }
Extra Info
In version 38, my code works.
If I turn off the component-model-async feature, this code also works.
Metadata
Metadata
Assignees
Labels
bugIncorrect behavior in the current implementation that needs fixingIncorrect behavior in the current implementation that needs fixingwasm-proposal:component-model-asyncIssues related to the WebAssembly Component Model async proposalIssues related to the WebAssembly Component Model async proposal
Type
Projects
Status
Backlog