You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
async: Cancellation of a host async call does not have the opportunity to gracefully return a value #12766
Currently in Wasmtime with component-model-async when a host function is cancelled the future representing that host function is simply dropped. I believe that the future gets a single final poll to produce a result before being dropped, but fundamentally Wasmtime doesn't provide embedders a means by which to gracefully handle the cancellation signal. This means that it's not easy or possible to impelement a sort of "atomic cancellation" in Wasmtime today where upon receiving cancellation some operation may already be complete but it is unable to communicate that. This is inspired by discussion here w.r.t cancelling UDP send operations.
Some possible shapes of a solution here are:
Instead of returning a return value, hosts could be provided an object through which the return value is transmitted. This models task.return more closely and bindings generation could paper over the differences here. This would enable hosts to at least provide a return value in a Drop implementation.
Hosts could be provided a sort of CancellationToken which has helper methods/etc to test/await it. Wasmtime would then not actually drop the future at all, but it would instead set the CancellationToken and continue polling as usual. It'd be up to embedders to drop futures, for example.
Should this all be gated behind some cancellation-token bindgen option?
That way, only the (few?) functions that need it can have their granular control. And the rest, where "atomicity" isn't a concern, remain being canceled automatically through dropping the future.
Instead of returning a return value, hosts could be provided an object through which the return value is transmitted
Do you see this as an internal implementation detail or will this also surface in the generated bindings?
It would be a nice bonus if such a cancellation token could replace the finish flag on the {Future,Stream}{Producer,Consumer} traits.
Agreed yeah, the current traits as-generated today I think should still be the default, and some form of opt-in would be needed for a different API. The Linker::func_wrap_concurrent function I think would change by default since it's lower-level, but even that could be adjusted to be a small wrapper around something else. I'm hesitant to add too many primitives though.
Do you see this as an internal implementation detail or will this also surface in the generated bindings?
Implementation detail, the base "thing" should be the most flexible, and then bindings can use that as they want.
It would be a nice bonus if such a cancellation token could replace the finish flag on the {Future,Stream}{Producer,Consumer} traits.
This might be pretty difficult given that it's either a poll-style context or async fn-style context (streams/futures are poll, host functions are async fn). Not saying it's impossible, but it's likely going to be a bit weirder.
Activity
badeend commentedon Mar 14, 2026
Should this all be gated behind some
cancellation-tokenbindgen option?That way, only the (few?) functions that need it can have their granular control. And the rest, where "atomicity" isn't a concern, remain being canceled automatically through dropping the future.
Do you see this as an internal implementation detail or will this also surface in the generated bindings?
It would be a nice bonus if such a cancellation token could replace the
finishflag on the{Future,Stream}{Producer,Consumer}traits.alexcrichton commentedon Mar 16, 2026
Agreed yeah, the current traits as-generated today I think should still be the default, and some form of opt-in would be needed for a different API. The
Linker::func_wrap_concurrentfunction I think would change by default since it's lower-level, but even that could be adjusted to be a small wrapper around something else. I'm hesitant to add too many primitives though.Implementation detail, the base "thing" should be the most flexible, and then bindings can use that as they want.
This might be pretty difficult given that it's either a
poll-style context orasync fn-style context (streams/futures arepoll, host functions areasync fn). Not saying it's impossible, but it's likely going to be a bit weirder.