Skip to content

guest-rust: Implement a Task handle for spawned futures - #1710

Merged
alexcrichton merged 7 commits into
bytecodealliance:mainfrom
adamrk:abk/task-handle
Sep 15, 2026
Merged

alexcrichton merged 7 commits into
bytecodealliance:mainfrom
adamrk:abk/task-handle

Conversation

@adamrk

@adamrk adamrk commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

The Task can be awaited to get the returned value of the future, or it can be dropped or explicitly canceled to cancel the future. It can also be detached to allow running in the background and match the previous behavior of spawn_local.

It's possible that the component-model task into which the future was spawned could be canceled while the Task referencing it is still alive. In that case, awaiting or canceling the task will return None.

Note: The naming and cancellation behavior here matches async_task::Task because that's what we were already using in wstd and it would allow switching to wit_bindgen::Task without requiring a wrapper. But I could also see an argument for going with the naming and behavior of tokio, so happy to switch to that if other people think it's better.

The `Task` can be awaited to get the returned value of the future, or it
can be dropped or explicitly canceled to cancel the future. It can also
be detached to allow running in the background and match the previous
behavior of `spawn_local`.

It's possible that the component-model task into which the future was
spawned could be canceled while the `Task` referencing it is still
alive. In that case, awaiting or canceling the task will return `None`.
Comment thread crates/guest-rust/src/rt/async_support/spawn.rs
Comment thread crates/guest-rust/src/rt/async_support/spawn.rs Outdated
Comment thread crates/guest-rust/src/rt/async_support/spawn.rs Outdated
@adamrk
adamrk requested a review from alexcrichton September 15, 2026 12:24

@alexcrichton alexcrichton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good to me! One final minor comment, and I think tests are timing out and/or infinite looping in CI

Comment on lines +154 to +168
if let Poll::Ready(()) = sender.poll_canceled(cx) {
return Poll::Ready(());
}
// SAFETY: `fut` has not been moved.
let fut = unsafe { Pin::new_unchecked(&mut inner.fut) };
match fut.poll(cx) {
Poll::Ready(t) => {
let _ = sender.send(t);
Poll::Ready(())
}
Poll::Pending => {
inner.sender = Some(sender);
Poll::Pending
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could the order of these be swapped to check-the-future then check-the-cancel? That way a race of the two has the value production always win (sort of a last-attempt)

@adamrk

adamrk commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Actually using close and poll_canceled on the oneshot can't give us the detach-on-drop semantics because dropping the receive end automatically closes the channel (current tests are failing/hanging because futures are unexpectedly cancelled). So I think we need the AbortHandle or a channel back the other way for cancellation.

@alexcrichton

Copy link
Copy Markdown
Member

Oh, right, of course! In that case AbortHandle seems fine and can always work on optimizing later if necessary

@alexcrichton
alexcrichton added this pull request to the merge queue Sep 15, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 15, 2026
@alexcrichton
alexcrichton added this pull request to the merge queue Sep 15, 2026
Merged via the queue into bytecodealliance:main with commit de65569 Sep 15, 2026
29 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.

2 participants