33This document contains a high-level summary of the native concurrency support
44added as part of [ WASI Preview 3] , providing background for understanding the
55definitions in the [ WIT] , [ AST explainer] , [ binary format] and [ Canonical ABI
6- explainer] documents that are gated by the 🔀 (async) and 🧵 (threading)
7- emojis. For an even higher-level introduction, see [ these] [ wasmio-2024 ]
8- [ presentations] [ wasmio-2025 ] .
6+ explainer] documents that are gated by the 🔀 (async) and 🧵 (threading) emojis.
97
108* [ Goals] ( #goals )
119* [ Summary] ( #summary )
@@ -16,6 +14,7 @@ emojis. For an even higher-level introduction, see [these][wasmio-2024]
1614 * [ Thread Built-ins] ( #thread-built-ins )
1715 * [ Thread-Local Storage] ( #thread-local-storage )
1816 * [ Blocking] ( #blocking )
17+ * [ Reentrance] ( #reentrance )
1918 * [ Waitables and Waitable Sets] ( #waitables-and-waitable-sets )
2019 * [ Streams and Futures] ( #streams-and-futures )
2120 * [ Stream Readiness] ( #stream-readiness )
@@ -56,8 +55,8 @@ concurrency-specific goals and use cases:
5655* Allow polyfilling in browsers via JavaScript Promise Integration ([ JSPI] )
5756* Avoid partitioning interfaces and components into separate ecosystems based
5857 on degree of concurrency; don't give components a "[ color] ".
59- * Maintain meaningful cross-language call stacks (for the benefit of debugging,
60- logging and tracing ).
58+ * Allow runtimes to maintain meaningful cross-language call stacks (for the
59+ benefit of debugging, logging, tracing and profiling ).
6160* Consider backpressure and cancellation as part of the design.
6261* Allow non-reentrant synchronous and event-loop-driven core wasm code that
6362 assumes a single global linear memory stack to not have to worry about
@@ -120,13 +119,14 @@ language's style of concurrency, most `world`s (including `wasi:cli/command`,
120119functions so that the contained Core WebAssembly code is free to block.
121120Implementing a non-` async ` function will primarily only arise when a component
122121is * virtualizing* the non-` async ` * imports* of a ` world ` (e.g., the getters and
123- setters of ` wasi:http/types.headers ` ). In this virtualization scenario (once
124- functions are allowed to be [ recursive] ( #TODO ) ), the Canonical ABI and/or Core
125- WebAssembly [ stack-switching] proposal will allow a parent component to
126- implement a child's non-` async ` imports in terms of the parent's ` async `
127- imports in the same manner as [ JSPI] . Thus, overall, ` async ` in WIT and the
128- Component Model does not behave like a "color" in the sense described by the
129- popular [ What Color Is Your Function?] essay.
122+ setters of ` wasi:http/types.headers ` ). In this more exotic virtualization
123+ scenario, a [ future extension] ( #TODO ) could allow a parent component that
124+ imports ` async ` functions to implement its child's non-` async ` imports in the
125+ same manner as [ JSPI] in the browser.
126+
127+ Thus, overall, ` async ` in WIT and the Component Model does not behave like a
128+ "color" in the sense described by the popular [ What Color Is Your Function?]
129+ essay.
130130
131131Each time a component export is called, the wasm runtime logically spawns a new
132132[ green thread] (as opposed to a [ kernel thread] ) to execute the export call
@@ -190,18 +190,23 @@ immediately block.
190190This backpressure mechanism provides the basis for how the sync and async ABIs
191191interoperate:
1921921 . If a component calls an import using the async ABI, and the import is
193- implemented by a component using the sync ABI, and the callee blocks,
194- execution is immediately transferred back to the caller (as required by the
195- async ABI) and the callee's component instance is marked "suspended".
196- 2 . If another async call attempts to start in a "suspended" component instance,
197- the Component Model automatically makes the call block, the same way as when
198- backpressure is active.
193+ implemented by a component using the sync ABI, the callee first acquires
194+ an "exclusive" on the component instance, and then starts executing. If
195+ the callee blocks, execution is immediately transferred back to the caller
196+ (as required by the async ABI).
197+ 2 . If another async call attempts to start in this same component instance, the
198+ callee immediately block when acquiring the "exclusive" lock, waiting for the
199+ previous call to return and release the lock.
199200
200201Note that because functions without ` async ` in their type are not allowed to
201- block, non-` async ` functions do not check for backpressure or suspension; they
202- always run synchronously . Components exporting a mix of ` async ` and non-` async `
202+ block, non-` async ` functions do not attempt to acquire the "exclusive" lock;
203+ they just barge in . Components exporting a mix of ` async ` and non-` async `
203204functions (which again mostly only arises in the more advanced virtualization
204- scenarios) must thus take care to handle non-` async ` reentrance gracefully.
205+ scenarios) must therefore take care to handle the "barges in" case gracefully.
206+ Because this nested non-` async ` call will complete synchronously without
207+ blocking, this behavior does not break [ component invariant] #3 since a single
208+ global shadow stack can still be (re)used in a LIFO manner to implement the
209+ nested synchronous call in much the same manner as a traditional signal handler.
205210
206211Lastly, WIT is extended with two new type constructors—` future<T> ` and
207212` stream<T> ` —to allow new WIT interfaces to explicitly represent concurrency in
@@ -314,20 +319,14 @@ of the new subtask created for the import call. Thus, one reason for
314319associating every thread with a "containing task" is to ensure that there is
315320always a well-defined async call stack.
316321
317- A semantically-observable use of the async call stack is to distinguish between
318- hazardous ** recursive reentrance** , in which a component instance is reentered
319- when one of its tasks is already on the callstack, from business-as-usual
320- ** sibling reentrance** , in which a component instance is reentered for the
321- first time on a particular async call stack. Recursive reentrance currently
322- always traps, but will be allowed (and indicated to core wasm) in an opt-in
323- manner in the [ future] ( #TODO ) .
324-
325- The async call stack is also useful for non-semantic purposes such as providing
326- backtraces when debugging, profiling and tracing. While particular languages
327- can and do maintain their own async call stacks in core wasm state, without the
328- Component Model's async call stack, linkage * between* different languages would
329- be lost at component boundaries, leading to a loss of overall context in
330- multi-component applications.
322+ The async call stack is not currently semantically observable to the running
323+ components other than possibly, nondeterministically, as part of the callstack
324+ stored in 📝 ` error-context ` . But this async call stack is also useful for
325+ other purposes such as providing backtraces when debugging, profiling and
326+ tracing. While particular languages can and do maintain their own async call
327+ stacks in core wasm state, without the Component Model's async call stack,
328+ linkage * between* different languages would be lost at component boundaries,
329+ leading to a loss of overall context in multi-component applications.
331330
332331There is an important gap between the Component Model's minimal form of
333332Structured Concurrency and the Structured Concurrency support that appears in
@@ -489,7 +488,11 @@ all of which are described above or below in more detail:
489488 [ ` subtask.cancel ` ] ( #cancellation ) built-in
490489
491490At each of these points, the [ current thread] ( #current-thread-and-task ) will be
492- suspended and execution will transfer to a caller's thread, if there is one.
491+ suspended and execution will transfer to a caller's thread, if there is one, or
492+ if not, the runtime, which may invoke new component exports or
493+ nondeterministically select a cooperative thread that is ready to run and resume
494+ it. Thus, each of these represents ** cooperative yield points** .
495+
493496Additionally, each of these potentially-blocking operations will trap if the
494497[ current task's function type] ( #current-thread-and-task ) does not declare the
495498` async ` effect, since only ` async ` -typed functions are allowed to block. As an
@@ -504,6 +507,24 @@ once the reason for blocking is addressed.
504507The [ Canonical ABI explainer] defines the above behavior more precisely; search
505508for ` may_block ` to see all the relevant points.
506509
510+ ### Reentrance
511+
512+ TODO:
513+ * mention Component Invariant #2
514+ * blocking specifically intended to allow reentrance
515+ * thus, define "reentrance" to only blocked synchronous
516+ * sync-typed, or async-lowered async-typed
517+ * sync-lowered async-typed = blocking
518+ * why useful
519+ * risk: async recursion => deadlock
520+ * specific example: backpressure (below)
521+ * lots of other possibilities
522+ * so general rule: don't create async recursion dependency
523+ * only possible via host or in guest via [ donut wrapping]
524+ * no well-defined way to rule out and trap; async call stack promising,
525+ only captures "causality", not "dependence" (good for observability,
526+ bad for catching recursion without false positives)
527+
507528### Waitables and Waitable Sets
508529
509530When an ` async ` -typed function is called with the async ABI and the call
@@ -662,6 +683,7 @@ without calling the component's Core WebAssembly code. By using a counter
662683instead of a boolean flag, unrelated pieces of code can report backpressure for
663684distinct limited resources without prior coordination.
664685
686+ TODO: mention component invariant #3 again
665687In addition to * explicit* backpressure set by wasm code, there is also an
666688* implicit* source of backpressure used to protect non-reentrant core wasm code.
667689In particular, when an export uses the sync ABI or the stackless async ABI, a
@@ -1379,21 +1401,20 @@ comes after:
13791401 type to block during instantiation
13801402* add an ` async ` effect on ` resource ` type definitions allowing a resource
13811403 type to block during its destructor
1382- * ` recursive ` function type attribute: allow a function to opt in to
1383- recursive [ reentrance ] , extending the ABI to link the inner and
1384- outer activations
1404+ * allow a parent component to perform [ JSPI ] -like suspension of the sync calls
1405+ of its child components, thereby allowing the parent to implement the child's
1406+ sync imports calls in terms of the parent's ` async ` imports.
13851407* add a ` strict-callback ` option that adds extra trapping conditions to
13861408 provide the semantic guarantees needed for engines to statically avoid
13871409 fiber creation at component-to-component ` async ` call boundaries
1410+ * allow function closures to be passed as first-class values, supporting the
1411+ the "callback" pattern in many pre-existing APIs, including Web APIs
13881412* allow pipelining multiple ` stream.read ` /` write ` calls
13891413* allow chaining multiple async calls together ("promise pipelining")
13901414* integrate with ` shared ` : define how to lift and lower functions ` async ` * and*
13911415 ` shared `
13921416
13931417
1394- [ wasmio-2024 ] : https://www.youtube.com/watch?v=y3x4-nQeXxc
1395- [ wasmio-2025 ] : https://www.youtube.com/watch?v=mkkYNw8gTQg
1396-
13971418[ Color ] : https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
13981419[ What Color Is Your Function? ] : https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
13991420[ Weak Memory Model ] : https://people.mpi-sws.org/~rossberg/papers/Watt,%20Rossberg,%20Pichon-Pharabod%20-%20Weakening%20WebAssembly%20[Extended].pdf
@@ -1426,6 +1447,7 @@ comes after:
14261447
14271448[ AST Explainer ] : Explainer.md
14281449[ Canonical Built-in ] : Explainer.md#canonical-built-ins
1450+ [ Component Invariant ] : Explainer.md#component-invariant
14291451[ `context.get` ] : Explainer.md#-contextget
14301452[ `context.set` ] : Explainer.md#-contextset
14311453[ `backpressure.inc` ] : Explainer.md#-backpressureinc-and-backpressuredec
@@ -1463,7 +1485,6 @@ comes after:
14631485[ Binary Format ] : Binary.md
14641486[ WIT ] : WIT.md
14651487[ Blast Zone ] : FutureFeatures.md#blast-zones
1466- [ Reentrance ] : Explainer.md#component-invariants
14671488[ `start` ] : Explainer.md#start-definitions
14681489
14691490[ Store ] : https://webassembly.github.io/spec/core/exec/runtime.html#syntax-store
0 commit comments