Remove the final "done" state of subtasks from the async ABI #425
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR has 2 commits, the first one is pure refactoring, no observable change (moving code from
Subtask
intocanon_lower
, which makes the logic easier to follow, imo), the second one removesCallState.DONE
.Before this PR, excusive ownership of borrowed handles is returned when a subtask (i.e.,
async
import call) reaches the "done" state. Abstractly this made sense, but working through source-language bindings, there is no good way of expressing when this event happens in the syntax, and this is an important event to express well (since it determines whether dropping/transferring the borrowed handle traps). Syntactically, the natural place to return borrows is when the subtask returns its value, which is the "returned" state (which precedes the "done" state), since that maps to, e.g., when aPromise
/Future
is visibly resolved. So that's what this PR does.With that change, there is no need for the "done" state, it just adds noise, so this PR removes the "done" state entirely, allowing the subtask to be dropped by its caller immediately after receiving the return value. This does mean that subtasks can continue executing (after
task.return
) even after their supertask finishes, but this doesn't break Structured Concurrency (and the existence of an unambiguous call-tree, which is semantically used by the reentrance guard) if we view the supertask as "async tail-calling" any still-executing subtasks when it exits. A blurb is added to Async.md by this PR explaining this more.Two other notes:
borrow
are tweaked (in CanonicalABI.md) to account for the fact that a supertask can now return before its subtasks have returned, so simply checking that aborrow
handle is passed to a nested subtask is no longer a sufficient condition to ensure proper nesting. However, the new rule is actually more expressive than the old rule, so it's probably what it should have been all along.borrow
insidestream<T>
orfuture<T>
anyways, this PR changes the validation rules in the proposal to match, which is a conservative change we relax in the future if someone actually finds a use case for this (I'm not aware of any, atm).