Async: add and decouple "waitable sets" from tasks #438
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.
Currently, every task has an implicit set of "waitables" (async import calls, async stream/future operations) that are collectively waited on by
task.wait
. However, for more advanced cases, different libraries may each need to own and manage their own sets of waitables independently of tasks. An example would a component that uses libc (which will need to maintain a set of waitables to implementselect()
) and contains a language runtime which implements its own async runtime that is not based on libc. This PR decouples waitable sets from tasks, making them first-class with new built-ins to create, use and destroy them.The PR is broken into two initial commits:
The first commit is pure refactoring, no semantic changes. The first commit introduces the
Waitable
andWaitableSet
classes and uses them to re-implement the current async functionality which makes the next commit much smaller. This ended up enabling a bunch of simplifications to the Python code that touches a bunch of code and descriptive text in CanonicalABI.md so it's a big commit and I'd recommend reading it separately from the next one (or skipping if you just want a summary of the semantic changes). Some interesting changes in this refactoring are:waitables
table until they are delivered to core wasm.*Handle
s, but*End
s. So "handles" are for resources, "ends" are for futures/streams, which avoids some ambiguity I've noticed when "handles" was used for both.The semantic changes in the second commit are:
task.wait
/task.poll
are removed.waitable-set.{new,wait,poll,drop}
are added. See Explainer.md for summaries/signatures.waitable.join
built-in is added to set/change/clear it.callback
ABI is changed so that an explicit waitable-set-index can be supplied (via return value). This ended up requiring a few supporting changes:i32
s through each call (using linear memory due to lack of multi-return...), the "context"i32
is removed and replaced by adding "context-local storage" in the form ofcontext.get
andcontext.set
built-ins. This is already a pending (breaking) change that would need to be made when we integratethread.spawn
, so doing it now just front-loads the breakage. See Async.md#context-local-storage for a better description.callback
and non-callback
ABIs, the packed return value for thecallback
ABI now has an opcode saying what to do: exit, yield, wait or poll. For wait/poll, the opcode is bit-packed with the waitable-set-index to wait/poll on.canon lower async
's bit-packing scheme is changed to be symmetric with thecallback
bitpacking scheme (reserving 4 bits for theCallState
and putting theCallState
in the low bits).CANCELLED
value ofCallState
, theEventCode
enum is renumbered to reserve a spot. AlsoYIELDED
is renamed toNONE
and given value0
to make it a natural sentinel value for thecallback
ABI after a yield or poll-with-no-event.task.*
namespace has been mostly evacuated and sincetask.backpressure
andtask.yield
are not actually task-related operations (backpressure is component-instance field and yielding (will) yield the current thread, not task), they are renamed tobackpressure.set
(in anticipation of a possiblebackpressure.get
if there's ever a use case for it) andyield
.