Skip to content

release borrows before task.return - #256

Merged
dicej merged 2 commits into
bytecodealliance:mainfrom
FrancoisLDaigneault:fix-async-borrow-release
Oct 30, 2026
Merged

dicej merged 2 commits into
bytecodealliance:mainfrom
FrancoisLDaigneault:fix-async-borrow-release

Conversation

@FrancoisLDaigneault

Copy link
Copy Markdown
Contributor

Async-lifted exports that receive a borrow<T> trap when the Python coroutine returns:

<unknown>!task.return ...
Caused by:
    borrow handles still remain at the end of the call

async_::call_task_return calls task.return first and releases the borrows afterwards. The Canonical ABI requires the opposite order: Task.return_ in design/mvp/canonical-abi/definitions.py runs trap_if(self.num_borrows > 0) before it resolves the task, and Concurrency.md ("Borrows") says that a callee which returns while num_borrows is greater than zero traps. Wasmtime 43 and later enforce this, and main builds against Wasmtime 48. The synchronous path already releases borrows before returning.

This change releases the borrows first, then calls task.return. A result type can't hold a borrow<T>, so lowering the results afterwards is unaffected.

The first commit adds the regression test: an async world export that takes a borrow of an imported resource, with its guest method and a host test. The second commit is the fix.

Fork runs of the Test workflow:

Fixes #255

FrancoisLD added 2 commits October 29, 2026 23:16
Add an async world export that takes a `borrow` of an imported resource,
and a host test that calls it.  The Canonical ABI requires every borrow
lent to a task to be released before `task.return`, so this currently
traps with "borrow handles still remain at the end of the call".
The Canonical ABI traps if a task returns while any borrow lent to it is
still outstanding.  `call_task_return` called `task.return` first and
released the borrows afterwards, so every async-lifted export that took a
`borrow` trapped with "borrow handles still remain at the end of the call"
on Wasmtime 43 and later.  Release the borrows first.  Results can't hold
borrows, so lowering them afterwards is unaffected.

Fixes bytecodealliance#255

@dicej dicej left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dicej
dicej merged commit 5382383 into bytecodealliance:main Oct 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async-lifted exports trap with "borrow handles still remain at the end of the call"

2 participants