Skip to content

component model: drop borrows when cancelling host tasks - #12631

Closed
jellevandenhooff wants to merge 1 commit into
bytecodealliance:mainfrom
jellevandenhooff:fix-subtask-cancel-borrow-release
Closed

component model: drop borrows when cancelling host tasks#12631
jellevandenhooff wants to merge 1 commit into
bytecodealliance:mainfrom
jellevandenhooff:fix-subtask-cancel-borrow-release

Conversation

@jellevandenhooff

@jellevandenhooff jellevandenhooff commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

Update the component model resource tracking to handle cancelling host tasks. Before this change, when cancelling a read on a UDP socket and then dropping the socket failed because of outstanding borrows: the read never released its borrow. Now it does.

I debugged this issue and wrote the code with Claude. I've tested this code for my use case and it seems to work, but I don't fully understand the implications. I worry that it might not be safe to release the borrow count because maybe the host function can still run.

@jellevandenhooff
jellevandenhooff requested a review from a team as a code owner February 20, 2026 23:07
@jellevandenhooff
jellevandenhooff requested review from alexcrichton and removed request for a team February 20, 2026 23:07
Per the component-model spec, "a resolved subtask has always dropped
all the borrowed handles that it was lent during the call"
(Concurrency.md). In the reference interpreter, `Subtask.deliver_resolve`
decrements the `num_lends` counts before the resolution event reaches
the caller.

When an async-lowered host function borrows a resource,
`resource_lift_borrow` increments the lend count on the owned resource
and records the lender in the host task's `CallContext`. Normally
`validate_scope_exit` undoes these lends when the task completes, but
`subtask.cancel` aborted the join handle without going through that
path.

Add `ResourceTables::cancel_scope` to release outstanding lends for a
cancelled host task, and call it from `subtask_cancel` after aborting
the join handle. A new test (`cancel_host_task_releases_borrow`)
exercises this by creating an owned resource, lending it to an
async host function, cancelling the subtask, and then dropping the
resource.
@jellevandenhooff
jellevandenhooff force-pushed the fix-subtask-cancel-borrow-release branch from 5407eb5 to bb386c7 Compare February 20, 2026 23:16
@github-actions github-actions Bot added the wasmtime:api Related to the API of the `wasmtime` crate itself label Feb 21, 2026
@alexcrichton

Copy link
Copy Markdown
Member

Thanks for the PR! Definitely looks like wrong behavior here, especially as the test shows. The fix isn't quite what I would have in mind for this, however, so I'll work a bit locally to see if I can't get something working. I'll likely have a sibling PR to this which includes your test as well. Will post back here when that's ready.

alexcrichton added a commit to alexcrichton/wasmtime that referenced this pull request Feb 23, 2026
This commit refactors some of the internals of `subtask.cancel` with
respect to host subtasks. Notably a few panics and semantic bugs are
fixed here. The main bug was that host subtasks could be aborted but
their completion might have still been queued up which would produce the
result somewhere or assert that the task exists. Cancellation is changed
to use `wait_for_event` to ensure that this completion is executed
before `subtask.cancel` returns. This helps keep host subtasks looking
more similar to guest subtasks in that respect.

Co-authored-by: Jelle van den Hooff <jelle@vandenhooff.name>

Closes bytecodealliance#12631
Closes bytecodealliance#12632
alexcrichton added a commit to alexcrichton/wasmtime that referenced this pull request Feb 23, 2026
This commit refactors some of the internals of `subtask.cancel` with
respect to host subtasks. Notably a few panics and semantic bugs are
fixed here. The main bug was that host subtasks could be aborted but
their completion might have still been queued up which would produce the
result somewhere or assert that the task exists. Cancellation is changed
to use `wait_for_event` to ensure that this completion is executed
before `subtask.cancel` returns. This helps keep host subtasks looking
more similar to guest subtasks in that respect.

Closes bytecodealliance#12631
Closes bytecodealliance#12632

Co-authored-by: Jelle van den Hooff <jelle@vandenhooff.name>
@alexcrichton

Copy link
Copy Markdown
Member

Ok I've pushed up a "more official fix" to #12640 which includes the tests here and should resolve them. Thanks again @jellevandenhooff!

github-merge-queue Bot pushed a commit that referenced this pull request Feb 23, 2026
This commit refactors some of the internals of `subtask.cancel` with
respect to host subtasks. Notably a few panics and semantic bugs are
fixed here. The main bug was that host subtasks could be aborted but
their completion might have still been queued up which would produce the
result somewhere or assert that the task exists. Cancellation is changed
to use `wait_for_event` to ensure that this completion is executed
before `subtask.cancel` returns. This helps keep host subtasks looking
more similar to guest subtasks in that respect.

Closes #12631
Closes #12632

Co-authored-by: Jelle van den Hooff <jelle@vandenhooff.name>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wasmtime:api Related to the API of the `wasmtime` crate itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants