Stream: git-wasmtime

Topic: wasmtime / issue #14286 WASI 0.2 pollable.block blocks th...


view this post on Zulip Wasmtime GitHub notifications bot (Sep 05 2026 at 08:24):

somdoron opened issue #14286:

When a component whose export is lifted with async blocks in WASI 0.2 (for
example wasi:io/poll@0.2's pollable.block or poll), the whole
Store stops until that call returns. Nothing else driven by
Store::run_concurrent makes progress in the meantime, and an async-lowered
call into that component comes back as RETURNED instead of STARTED. The
same component blocking through wasi:clocks@0.3 wait-for behaves as
expected: only its own task suspends and other tasks keep running.

This matters for mixing WASI 0.2 and 0.3 components in one store: any 0.2
component that waits on I/O effectively serializes everything around it.

Test Case

Standalone reproduction (single cargo run, builds and composes the components
itself): https://github.com/somdoron/wasmtime-p2-block-repro

Attached separately for convenience (with a .txt suffix so GitHub accepts them):

Each core module is turned into a component with
wit_component::embed_component_metadata + ComponentEncoder against its
world, then the two are composed with wac plug (component 1 into component 2).

Steps to Reproduce

Expected Results

The async-lowered call into component 1 returns STARTED, component 1 suspends
on its own task inside pollable.block, the 100ms timer fires first, total
≈300ms.

Concurrency.md, "Blocking":

When a thread calls an import using the async ABI, the Component Model
guarantees that if the callee task blocks, control flow is immediately
returned back to the caller's thread.

If instead the task has not yet returned a value and the callee's function
type declares the async effect, control flow returns directly to the caller.
If the caller used the async ABI, then control flow returns to Core
WebAssembly, indicating that the call "blocked" by returning the non-zero
index of a new subtask.

sleep is async in WIT and component 2 lowers it with async, so once
component 1's task blocks the call must come back to component 2 as STARTED.
The same section does leave it to the host to decide what counts as blocking
for host-implemented imports ("what counts as 'blocking' is up to the host"),
but Wasmtime already treats the WASI 0.3 equivalent as blocking, and a
300ms timer wait is hard to see as anything else.

The spec is also explicit that the WASI 0.2 sync ABI is meant to keep working
inside this model rather than being a separate, non-concurrent world
(Summary
and Goals):

The new async ABI can be used alongside or instead of the existing WASI 0.2
"sync ABI" to call or implement any async-typed functions. When calling
an imported function via the async ABI, if the async callee blocks, control
flow is returned immediately to the caller, and the callee continues
executing concurrently. [...] Critically, both sync-ABI-calls-async-ABI and
async-ABI-calls-sync-ABI pairings have well-defined, composable behavior for
both inter-component and intra-component calls.

Avoid partitioning interfaces and components into separate ecosystems based
on degree of concurrency; don't give components a "color".

Component 1 here is exactly the "traditional sync code compiled to a component
exporting async functions" case the Summary describes, except that its sync
imports are WASI 0.2 rather than WASI 0.3.

Actual Results

The async-lowered call blocks for the full 300ms and returns RETURNED. The
100ms timer hasn't even been started yet when it returns. Total ≈400ms.

[   0ms] component 2: go entered
[   1ms] component 1: entered
[ 303ms] component 1: woke                                       <-- 300ms, store idle
[ 303ms] component 2:   LONG  (component 1) call status = 2      <-- RETURNED, not STARTED
[ 304ms] component 2:   SHORT (p3 timer)     call status = 17    <-- STARTED, subtask 1
[ 406ms] component 2: EVENT from subtask 1
[ 406ms] component 2: go finished
total 406ms

Blocking in wasi:io/poll@0.2.6#poll instead of pollable.block behaves the
same.

Versions and Environment

Wasmtime version or commit: 45.0.3; also reproduced on main at 0754181e4b

Operating system: macOS

Architecture: aarch64

Extra Info

I see two possible directions and would appreciate guidance on which one is
preferred:

Happy to work on either.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 05 2026 at 08:24):

somdoron added the bug label to Issue #14286.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 08 2026 at 16:48):

pchickey commented on issue #14286:

Unfortunately, our project doesn't plan to support either of these options. The solution for users is to use WASI 0.3 interfaces if they need to run stores concurrently.

WASI 0.2 predates component model concurrency, and component model concurrency was designed to improve on many aspects of the WASI 0.2 pollable and stream design. The WASI 0.3 interfaces and implementation have major changes in order to support CM concurrency, and based on our experiences in the 0.1 to 0.2 transition, we have decided that making compatibility between versions with either enhancements to the host (your option 1) or component adapters (option 2) are both very challenging for us to create and maintain, and have significant drawbacks for our users.

Right now, targeting WASI 0.3 from guest code is limited by the maturity of guest toolchain integration. Wasi-sdk now supports 0.3 for C/C++ guests and Rust nightly has a wasm32-wasip3 target as well, and will ride the Rust release trains towards availability in stable at the end of this year. Other language toolchains with p3 support exist as well but I'm not totally up to date on their statuses. So, we advise you approach this problem by upgrading your guests, and if you run into issues with particular toolchains you can file issues and/or contribute fixes there.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 08 2026 at 18:13):

somdoron closed issue #14286:

When a component whose export is lifted with async blocks in WASI 0.2 (for
example wasi:io/poll@0.2's pollable.block or poll), the whole
Store stops until that call returns. Nothing else driven by
Store::run_concurrent makes progress in the meantime, and an async-lowered
call into that component comes back as RETURNED instead of STARTED. The
same component blocking through wasi:clocks@0.3 wait-for behaves as
expected: only its own task suspends and other tasks keep running.

This matters for mixing WASI 0.2 and 0.3 components in one store: any 0.2
component that waits on I/O effectively serializes everything around it.

Test Case

Standalone reproduction (single cargo run, builds and composes the components
itself): https://github.com/somdoron/wasmtime-p2-block-repro

Attached separately for convenience (with a .txt suffix so GitHub accepts them):

Each core module is turned into a component with
wit_component::embed_component_metadata + ComponentEncoder against its
world, then the two are composed with wac plug (component 1 into component 2).

Steps to Reproduce

Expected Results

The async-lowered call into component 1 returns STARTED, component 1 suspends
on its own task inside pollable.block, the 100ms timer fires first, total
≈300ms.

Concurrency.md, "Blocking":

When a thread calls an import using the async ABI, the Component Model
guarantees that if the callee task blocks, control flow is immediately
returned back to the caller's thread.

If instead the task has not yet returned a value and the callee's function
type declares the async effect, control flow returns directly to the caller.
If the caller used the async ABI, then control flow returns to Core
WebAssembly, indicating that the call "blocked" by returning the non-zero
index of a new subtask.

sleep is async in WIT and component 2 lowers it with async, so once
component 1's task blocks the call must come back to component 2 as STARTED.
The same section does leave it to the host to decide what counts as blocking
for host-implemented imports ("what counts as 'blocking' is up to the host"),
but Wasmtime already treats the WASI 0.3 equivalent as blocking, and a
300ms timer wait is hard to see as anything else.

The spec is also explicit that the WASI 0.2 sync ABI is meant to keep working
inside this model rather than being a separate, non-concurrent world
(Summary
and Goals):

The new async ABI can be used alongside or instead of the existing WASI 0.2
"sync ABI" to call or implement any async-typed functions. When calling
an imported function via the async ABI, if the async callee blocks, control
flow is returned immediately to the caller, and the callee continues
executing concurrently. [...] Critically, both sync-ABI-calls-async-ABI and
async-ABI-calls-sync-ABI pairings have well-defined, composable behavior for
both inter-component and intra-component calls.

Avoid partitioning interfaces and components into separate ecosystems based
on degree of concurrency; don't give components a "color".

Component 1 here is exactly the "traditional sync code compiled to a component
exporting async functions" case the Summary describes, except that its sync
imports are WASI 0.2 rather than WASI 0.3.

Actual Results

The async-lowered call blocks for the full 300ms and returns RETURNED. The
100ms timer hasn't even been started yet when it returns. Total ≈400ms.

[   0ms] component 2: go entered
[   1ms] component 1: entered
[ 303ms] component 1: woke                                       <-- 300ms, store idle
[ 303ms] component 2:   LONG  (component 1) call status = 2      <-- RETURNED, not STARTED
[ 304ms] component 2:   SHORT (p3 timer)     call status = 17    <-- STARTED, subtask 1
[ 406ms] component 2: EVENT from subtask 1
[ 406ms] component 2: go finished
total 406ms

Blocking in wasi:io/poll@0.2.6#poll instead of pollable.block behaves the
same.

Versions and Environment

Wasmtime version or commit: 45.0.3; also reproduced on main at 0754181e4b

Operating system: macOS

Architecture: aarch64

Extra Info

I see two possible directions and would appreciate guidance on which one is
preferred:

Happy to work on either.

view this post on Zulip Wasmtime GitHub notifications bot (Sep 08 2026 at 18:13):

somdoron commented on issue #14286:

Thank you, it makes total sense. Closing this.


Last updated: Sep 20 2026 at 18:08 UTC