dicej opened issue #11833:
The component model async ABI supports cancelling subtasks, and this is implemented in Wasmtime for guests via the
subtask.cancelintrinsic. However, there's not yet any host-level API for cancelling tasks created usingcall_concurrent.Note that subtask cancellation is both cooperative and asynchronous such that the guest may require arbitrary time and/or I/O to handle a cancellation request; in extreme cases, it may be busy looping and not even receive the request, or it might receive it and ignore it. Therefore, the host needs some way to know when the task has actually exited after sending it a cancellation request. In addition, since a task may continue running after it has returned a result, the host may need to cancel it either before it returns or after.
Currently,
call_concurrentalready returns aTaskExitobject which may be used to wait (asynchronously) for the task to exit. However, the caller must wait until thecall_concurrentFutureresolves before it can get access to it.Here's a rough sketch of an API that might work:
[Typed]Func::call_concurrentreturns a customFutureimplementation with anasync fn cancel(self, accessor: impl AsAccessor) -> Result<Option<R>, TaskExit>function, whereRis the return type andOption<R>represents the possibility that the task callstask.returninstead oftask.cancelafter receiving the cancel request.TaskExitgets a newasync fn cancel(self, accessor: impl AsAccessor) -> Result<()>function which allows the caller to cancel a task (or wait just wait for it to exit it we've already sent it a cancel request) after it has returned a value.Note that the host can always simply drop the instance if the guest refuses to cooperate promptly with a cancel request.
dicej added the wasm-proposal:component-model-async label to Issue #11833.
dicej edited issue #11833:
The component model async ABI supports cancelling subtasks, and this is implemented in Wasmtime for guests via the
subtask.cancelintrinsic. However, there's not yet any host-level API for cancelling tasks created usingcall_concurrent.Note that subtask cancellation is both cooperative and asynchronous such that the guest may require arbitrary time and/or I/O to handle a cancellation request; in extreme cases, it may be busy looping and not even receive the request, or it might receive it and ignore it. Therefore, the host needs some way to know when the task has actually exited after sending it a cancellation request. In addition, since a task may continue running after it has returned a result, the host may need to cancel it either before it returns or after.
Currently,
call_concurrentalready returns aTaskExitobject which may be used to wait (asynchronously) for the task to exit. However, the caller must wait until thecall_concurrentFutureresolves before it can get access to it.Here's a rough sketch of an API that might work:
[Typed]Func::call_concurrentreturns a customFutureimplementation with anasync fn cancel(self, accessor: impl AsAccessor) -> Result<Option<R>, TaskExit>function, whereRis the return type andOption<R>represents the possibility that the task callstask.returninstead oftask.cancelafter receiving the cancel request.TaskExitgets a newasync fn cancel(self, accessor: impl AsAccessor) -> Result<()>function which allows the caller to cancel a task (or wait just wait for it to exit if we've already sent it a cancel request) after it has returned a value.Note that the host can always simply drop the instance if the guest refuses to cooperate promptly with a cancel request.
dicej commented on issue #11833:
Note that, once we have this, we can and should use it when timing out instances in
wasmtime serveto allow them a bit of extra time to shut down gracefully.
alexcrichton commented on issue #11833:
For the bikeshed, I'm getting a bit worried about the contortions necessary to get access to these events and such. For example there's two
cancelfunctions here and technically it's impossible to call the originalcancelfunction if it's on the future itself, or at least it's difficult to because you want to simultaneously wait on the future (which naively takes ownership of the future) and then also retain the ability to cancel.How about something like this instead:
impl Func { fn call(&self, store: impl AsAccessor) -> (AsyncTask, impl Future<Output = Result<()>> + '_) { // ... } } impl AsyncTask { async fn task_done(&self, store: impl AsAccesor); fn cancel(&self, store: impl AsAccesor); }The properties being:
- The task metadata is available immediately, not just after the function returns
- The task can be cancelled at any time through the task handle
- Cancellation is modeled as when the
callfuture resolves. I'm thinking we make a custom error to represent cancellation and embedders downcast to test it.
tschneidereit commented on issue #11833:
Discussing this with Alex, we decided to move this to post-p3. The key improvement here is the ability to send soft-cancellation notifications to content on e.g. budget exhaustion, which we don't think needs to block the release.
Last updated: Dec 06 2025 at 07:03 UTC