Stream: general

Topic: Async component calls with exclusive locks on their stores


view this post on Zulip Celarye (Sep 26 2025 at 16:49):

I am currently changing my codebase (an the components it uses) to support Wasmtime's component-model-async feature and am wondering how async component calls will work, as currently every call into a component requires a mutable reference to its store which is exclusive?

Example code snippet from one of my projects on how I currently handle this:

pub async fn call_event(
        &self,
        plugin_name: &str,
        event: &DiscordEvents,
    ) -> Result<Vec<DiscordRequests>, ()> {
        let plugins = self.plugins.read().await;
        let plugin = plugins.get(plugin_name).unwrap();

        match plugin
            .instance
            .discord_bot_plugin_plugin_functions()
            .call_discord_event(&mut *plugin.store.lock().await, event) // plugin.store is of the type Mutex<Store<InternalRuntime>>
            .await
        {
            Ok(call_result) => match call_result {
                Ok(discord_requests) => Ok(discord_requests),
                Err(err) => {
                    error!("The plugin returned an error: {}", &err);
                    Err(())
                }
            },
            Err(err) => {
                error!("Something went wrong while calling the plugin: {}", &err);
                Err(())
            }
        }
    }

I assume this is a pretty negligent question so feel free to link me to resources which explain/expand upon this topic (any resources regarding wasmtime's v36/37 WASIp3 changes are welcome as well).

NOTE: I have done some more research before posting and have found wasmtime::component::Access and its new function, it seems that this might solve my issue as I can also pass it to my component call function instead of the Store itself? I am just not sure how to implement it.

view this post on Zulip Alex Crichton (Sep 26 2025 at 19:49):

I assume this is a pretty negligent question so feel free to link me to resources which explain/expand upon this topic

Heh no worries at all! WASIp3 isn't released yet so we're still in the lead-up phase where we're getting things in place, and docs are one of those things that only scantily exists so far. It's mostly "documented" through examples and tests in the Wasmtime repo.

currently every call into a component requires a mutable reference to its store which is exclusive

The basic answer to this question is: we solved this by not taking a mutable reference. Instance::run_concurrent sort of transforms a StoreContextMut into an Accessor which is then threaded around to functions such as Func::call_concurrent to achieve concurrent execution.

To get access to this in bindgen-generated bindings you'll either want the WIT function to specify that it's async (e.g. export foo: async func();) or you'll want to specify exports: { default: store } in your bindgen! configuration

view this post on Zulip Celarye (Sep 27 2025 at 15:59):

This leaves me with two main issues/questions.

1.

I am not sure how to incorporate this into my current design as run_concurrent still requires a mutable reference to a Store. Is there a way I can instead maybe store an Accessor in a struct which I can then just re-use? Maybe I am missing a way to make this work with run_concurrent?


2.

Before I had:

wasmtime::component::bindgen!({ imports: { default: async }, exports: { default: async }});

Now I tried to replace the above line with:

wasmtime::component::bindgen!();

And instead I added the asynckeyword to all my host and component functions in my WIT files.

2. this trait has no implementations, consider adding one [E0277]

Meaning that what I ended up with is that I only added the asynckeyword to my component functions and:

wasmtime::component::bindgen!({ imports: { default: async }});

Is this intended behavior?

view this post on Zulip Celarye (Sep 28 2025 at 17:03):

I am actually not sure where I should get access to the run_concurrent method using bingen!, I also tried exports: { default: store } (alongside the async keyword) but could not find it.

Image as example for where I try to access it, Plugin is the bindgen! generated Instance: image.png

This is kind of related to my second question above but anywhere I can find more docs on the bindgen! options like exports: { default: store } or exports: { default: tracing }(curious what it's for). No worries if there aren't any yet!


Last updated: Dec 06 2025 at 05:03 UTC