Stream: git-wasmtime

Topic: wasmtime / issue #10573 Calling multiple functions result...


view this post on Zulip Wasmtime GitHub notifications bot (Apr 13 2025 at 18:32):

Cruxial0 opened issue #10573:

I am in the process of writing a plugin system in Rust using wasmtime. I use WIT to define a contract. In the contract I have an interface defined as:

interface events {
    on-permissions-granted: func();
    on-plugin-loaded: func();
}

The idea is to call these event functions from my host, then let plugins do whatever they need to do. Strangely enough, it works with a single function, but when I add a second function I run into the error: wasm trap: cannot enter component instance. My current code is as follows:

pub async fn initialize(&mut self) -> Result<(), Box<dyn std::error::Error>> {
    log::info!("Initializing plugin manager");

    self.load_plugins()?;

    let mut loaded_instances = Vec::new();
    for plugin in self.plugins.clone() {
        let instance = self.instantiate_plugin(plugin.clone()).await?;
        loaded_instances.push(instance);
    }

    for instance in &loaded_instances {
        self.push_active_plugin(instance.clone());
        self.on_plugin_loaded(&instance).await?;

        // Check permissions
        let resolved = PERMISSION_MANAGER.with(|pm| {
            let mut manager = pm.borrow_mut();
            match manager.add_plugin(instance.plugin.clone()) {
                Ok(state) => state == PermissionState::Granted,
                Err(_) => false,
            }
        });

        if resolved {
            self.on_permissions_granted(&instance).await?;
        }
    }

    self.plugins = loaded_instances.iter().map(|i| i.plugin.clone()).collect();

    Ok(())
}
trait PluginEvents {
    async fn on_plugin_loaded(&mut self, instance: &PluginInstance) -> Result<(), Box<dyn std::error::Error>>;
    async fn on_permissions_granted(&mut self, instance: &PluginInstance) -> Result<(), Box<dyn std::error::Error>>;
}

impl PluginEvents for PluginManager {
    async fn on_plugin_loaded(&mut self, instance: &PluginInstance) -> Result<(), Box<dyn std::error::Error>> {
        let load_func = get_typed_function::<(), ()>(instance, "events:on-plugin-loaded", &mut self.store)?;
        match load_func.call_async(&mut self.store, ()).await {
            Ok(_) => (),
            Err(e) => log::warn!("Failed to call on-plugin-loaded: {}", e),
        }
        Ok(())
    }

    async fn on_permissions_granted(&mut self, instance: &PluginInstance) -> Result<(), Box<dyn std::error::Error>> {
        let perm_func = get_typed_function::<(), ()>(instance, "events:on-permissions-granted", &mut self.store)?;
        match perm_func.call_async(&mut self.store, ()).await {
            Ok(_) => (),
            Err(e) => log::warn!("Failed to call on-permissions-granted: {}", e), // error occurs here
        }
        Ok(())
    }
}

(note: get_typed_function is my own wrapper function)

I know there's been similar issues in the past, notably #8670, which I have read, but the use case seems different from mine. Is there any way to fix this issue, or is it a limitation within wasmtime?

view this post on Zulip Wasmtime GitHub notifications bot (Apr 13 2025 at 21:03):

bjorn3 commented on issue #10573:

I believe you need to call .post_return_async() on the TypedFunc after the .call_async() has returned and before you call into the component again.

view this post on Zulip Wasmtime GitHub notifications bot (Apr 13 2025 at 21:31):

Cruxial0 closed issue #10573:

I am in the process of writing a plugin system in Rust using wasmtime. I use WIT to define a contract. In the contract I have an interface defined as:

interface events {
    on-permissions-granted: func();
    on-plugin-loaded: func();
}

The idea is to call these event functions from my host, then let plugins do whatever they need to do. Strangely enough, it works with a single function, but when I add a second function I run into the error: wasm trap: cannot enter component instance. My current code is as follows:

pub async fn initialize(&mut self) -> Result<(), Box<dyn std::error::Error>> {
    log::info!("Initializing plugin manager");

    self.load_plugins()?;

    let mut loaded_instances = Vec::new();
    for plugin in self.plugins.clone() {
        let instance = self.instantiate_plugin(plugin.clone()).await?;
        loaded_instances.push(instance);
    }

    for instance in &loaded_instances {
        self.push_active_plugin(instance.clone());
        self.on_plugin_loaded(&instance).await?;

        // Check permissions
        let resolved = PERMISSION_MANAGER.with(|pm| {
            let mut manager = pm.borrow_mut();
            match manager.add_plugin(instance.plugin.clone()) {
                Ok(state) => state == PermissionState::Granted,
                Err(_) => false,
            }
        });

        if resolved {
            self.on_permissions_granted(&instance).await?;
        }
    }

    self.plugins = loaded_instances.iter().map(|i| i.plugin.clone()).collect();

    Ok(())
}
trait PluginEvents {
    async fn on_plugin_loaded(&mut self, instance: &PluginInstance) -> Result<(), Box<dyn std::error::Error>>;
    async fn on_permissions_granted(&mut self, instance: &PluginInstance) -> Result<(), Box<dyn std::error::Error>>;
}

impl PluginEvents for PluginManager {
    async fn on_plugin_loaded(&mut self, instance: &PluginInstance) -> Result<(), Box<dyn std::error::Error>> {
        let load_func = get_typed_function::<(), ()>(instance, "events:on-plugin-loaded", &mut self.store)?;
        match load_func.call_async(&mut self.store, ()).await {
            Ok(_) => (),
            Err(e) => log::warn!("Failed to call on-plugin-loaded: {}", e),
        }
        Ok(())
    }

    async fn on_permissions_granted(&mut self, instance: &PluginInstance) -> Result<(), Box<dyn std::error::Error>> {
        let perm_func = get_typed_function::<(), ()>(instance, "events:on-permissions-granted", &mut self.store)?;
        match perm_func.call_async(&mut self.store, ()).await {
            Ok(_) => (),
            Err(e) => log::warn!("Failed to call on-permissions-granted: {}", e), // error occurs here
        }
        Ok(())
    }
}

(note: get_typed_function is my own wrapper function)

I know there's been similar issues in the past, notably #8670, which I have read, but the use case seems different from mine. Is there any way to fix this issue, or is it a limitation within wasmtime?

view this post on Zulip Wasmtime GitHub notifications bot (Apr 13 2025 at 21:31):

Cruxial0 commented on issue #10573:

I believe you need to call .post_return_async() on the TypedFunc after the .call_async() has returned and before you call into the component again.

Okay yeah, that's it. It even clearly states so in the documentation. Not sure how I missed that... Thanks.


Last updated: Dec 06 2025 at 06:05 UTC