Add built-ins for creating/switching cooperative threads #557
+2,381
−1,595
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 extends the concurrency support in Preview 3 with the ability for core wasm to create and switch between "cooperative threads", providing fiber-like functionality that is exposed to core wasm as plain core function imports that are integrated with and built on the async machinery that is already part of Preview 3.
Cooperative threads are meant to be released in some 0.3.x minor version after 0.3.0, so they are given a separate emoji-gate than 0.3.0's 🔀. Since cooperative threads pave the way for preemptive threads (which depend on shared-everything-threads), the emoji-gate for cooperative threads is 🧵 and preemptive threads are switched to 🧵②. The current emoji-gates 🚝 and 🚟 are folded into 🧵 since they're also post-0.3.0 and make sense only with cooperative threads but if there's still a reason to keep them separate, we can split them back out.
This feature is intended to cover both "host threads" (e.g., pthreads.h) and "green threads" (e.g. goroutines and Java virtual threads) use cases. The former use case mostly only needs the ability to spawn a thread (with, in a cooperative setting, the choice between whether to run the new thread immediately or "later"). But the green-threads use case needs to take explicit control of switching between threads (instead of leaving it up to the wasm runtime), so several additional built-ins are added to do this such as
thread.switch-to
(which works like theswitch
instruction in the stack-switching proposal). For the summary of the built-ins, see this section. Also see the rewritten goals and summary inConcurrency.md
(formerlyAsync.md
).With the above new built-ins,
thread.spawn_indirect
becomes an optimized fusion ofthread.new_indirect
+thread.resume-later
. For simplicity,thread.spawn_indirect
is kept gated to 🧵②, so that it's "the one that depends onshared
" andthread.new_indirect
is added by 🧵 and has noshared
option for now. As noted in the Binary format TODO, as part of 1.0-rc, we'll need to add ashared?
option to every existing built-in (so that they can all be called from preemptive threads) anyhow. But this PR does change the binary format forthread.spawn_indirect
(to include an optionalshared
immediate) so CC @abrown on these changes in Binary.md. (It's fine to not implement them any time soon, but I think it's useful to see the sketch of how they'd eventually look.)For consistency, the
yield
built-in is backwards-compatibly renamedthread.yield
(keeping the opcode the same and keepingyield
in the text format but marked "deprecated" until the transition is done).What was previously called "context-local storage" in the explainer is now renamed to "thread-local storage" (since it's now literally stored per thread), but the
context.{get,set}
built-ins keep the same name since they still seem to make sense as names. As planned earlier, the static length of the thread-local storage array is bumped from "1" to "2" b/c now there's a good reason to store the "linear memory shadow stack pointer" alongside the "general TLS struct pointer".Although a bunch of the explanatory prose changes in this PR, the actual functional addition to what was already necessary for 0.3.0 is surprisingly little; there's mostly just this new ability to directly
thread.switch-to
. Hopefully that remains true in the implementation.I expect to keep this PR open for a while until someone with an implementor's hat on has a chance to look carefully, so no rush for folks to review. I'd suggest reading the diff of
Concurrency.md
first thenExplainer.md
thenCanonicalABI.md
.