Skip to content

Commit ddc8f65

Browse files
committed
Improve Concurrency.md ordering and wording
1 parent cfc6e54 commit ddc8f65

File tree

3 files changed

+68
-69
lines changed

3 files changed

+68
-69
lines changed

design/mvp/CanonicalABI.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4742,11 +4742,11 @@ def canon_thread_available_parallelism():
47424742
[Shared-Everything Dynamic Linking]: examples/SharedEverythingDynamicLinking.md
47434743
[Concurrency Explainer]: Concurrency.md
47444744
[Suspended]: Concurrency#waiting
4745-
[Structured Concurrency]: Concurrency.md#structured-concurrency
4745+
[Structured Concurrency]: Concurrency.md#subtasks-and-supertasks
47464746
[Backpressure]: Concurrency.md#backpressure
47474747
[Current Thread]: Concurrency.md#current-thread-and-task
47484748
[Current Task]: Concurrency.md#current-thread-and-task
4749-
[Subtasks]: Concurrency.md#structured-concurrency
4749+
[Subtasks]: Concurrency.md#subtasks-and-supertasks
47504750
[Readable and Writable Ends]: Concurrency.md#streams-and-futures
47514751
[Readable or Writable End]: Concurrency.md#streams-and-futures
47524752
[Thread-Local Storage]: Concurrency.md#thread-local-storage

design/mvp/Concurrency.md

Lines changed: 65 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ emojis. For an even higher-level introduction, see [these][wasmio-2024]
1111
* [Summary](#summary)
1212
* [Concepts](#concepts)
1313
* [Threads and Tasks](#threads-and-tasks)
14+
* [Subtasks and Supertasks](#subtasks-and-supertasks)
1415
* [Current Thread and Task](#current-thread-and-task)
1516
* [Thread-Local Storage](#thread-local-storage)
16-
* [Structured concurrency](#structured-concurrency)
1717
* [Streams and Futures](#streams-and-futures)
1818
* [Stream Readiness](#stream-readiness)
1919
* [Waiting](#waiting)
@@ -236,76 +236,17 @@ Thread
236236
where a **component store** is the top-level "thing" and analogous to a Core
237237
WebAssembly [store].
238238

239-
The reason for the thread/task split is so that, when one thread creates a new
240-
thread by calling [`thread.new-indirect`], the new thread is contained by the
241-
task of the original thread. Thus there is an N:1 relationship between threads
242-
and tasks that ties N threads to the original export call (= "task") that
243-
transitively spawned those N threads. This relationship serves several purposes
244-
described in the following sections.
239+
The reason for the thread/task split is that, when one thread creates a new
240+
thread, the new thread is contained by the task of the original thread which
241+
creates an N:1 relationship between threads and tasks that ties N threads to
242+
the original export call (= "task") that transitively spawned those N threads.
243+
This relationship serves several purposes described in the following sections.
245244

246245
In the Canonical ABI explainer, threads, tasks, component instances and
247246
component stores are represented by the [`Thread`], [`Task`],
248247
[`ComponentInstance`] and [`Store`] classes, resp.
249248

250-
### Current Thread and Task
251-
252-
At any point in time while executing Core WebAssembly code or a [canonical
253-
built-in] called by Core WebAssembly code, there is a well-defined **current
254-
thread** whose containing task is the **current task**. The "current thread" is
255-
modelled in the Canonical ABI's Python code by explicitly passing a [`Thread`]
256-
object as an argument to all function calls so that the semantic "current
257-
thread" is always the value of the `thread` parameter. Threads store their
258-
containing task so that the "current task" is always `thread.task`.
259-
260-
### Thread-Local Storage
261-
262-
Each thread contains a distinct mutable **thread-local storage** array. The
263-
current thread's thread-local storage can be read and written from core wasm
264-
code by calling the [`context.get`] and [`context.set`] built-ins.
265-
266-
The thread-local storage array's length is currently fixed to contain exactly
267-
2 `i32`s with the goal of allowing this array to be stored inline in whatever
268-
existing runtime data structure is already efficiently reachable from ambient
269-
compiled wasm code. Because module instantiation is declarative in the
270-
Component Model, the imported `context.{get,set}` built-ins can be inlined by
271-
the core wasm compiler as-if they were instructions, allowing the generated
272-
machine code to be a single load or store. This makes thread-local storage a
273-
natural place to store:
274-
1. a pointer to the linear-memory "shadow stack" pointer
275-
2. a pointer to a struct used by the runtime to implement the language's
276-
thread-local features
277-
278-
When threads are created explicitly by `thread.new-indirect`, the lifetime of
279-
the thread-local storage array ends when the function passed to
280-
`thread.new-indirect` returns and thus any linear-memory allocations associated
281-
with the thread-local storage array should be eagerly freed by guest code right
282-
before returning. Similarly, since each call to an export logically creates a
283-
fresh thread, thread-local allocations can be eagerly released when this
284-
implicit thread exits by returning from the exported function or, if the
285-
stackless async ABI is used, returning the "exit" code to the event loop. This
286-
non-reuse of thread-local storage between distinct export calls avoids what
287-
would otherwise be a likely source of TLS-related memory leaks.
288-
289-
When [memory64] is integrated into the Component Model's Canonical ABI,
290-
`context.{get,set}` will be backwards-compatibly relaxed to allow `i64`
291-
pointers (overlaying the `i32` values like hardware 32/64-bit registers). When
292-
[wasm-gc] is integrated, these integral context values can serve as indices
293-
into guest-managed tables of typed GC references.
294-
295-
Since the same mutable thread-local storage cells are shared by all core wasm
296-
running under the same thread in the same component, the cells' contents must
297-
be carefully coordinated in the same way as native code has to carefully
298-
coordinate native ISA state (e.g., the [FS or GS segment base address]). In the
299-
common case, thread-local storage is only `context.set` by the entry trampoline
300-
invoked by [`canon_lift`] and then all transitively reachable core wasm code
301-
(including from any `callback`) assumes `context.get` returns the same value.
302-
Thus, if any *non*-entry-trampoline code calls `context.set`, it is the
303-
responsibility of *that code* to restore this default assumption before
304-
allowing control flow to escape into the wild.
305-
306-
For more information, see [`context.get`] in the AST explainer.
307-
308-
### Structured concurrency
249+
### Subtasks and Supertasks
309250

310251
As mentioned above, calling a component export creates a task to track the
311252
state used to enforce Canonical ABI rules that apply to the callee (an example
@@ -375,6 +316,64 @@ For scenarios where one component wants to *non-cooperatively* put an upper
375316
bound on execution of a call into another component, a separate "[blast zone]"
376317
feature is necessary in any case (due to iloops and traps).
377318

319+
### Current Thread and Task
320+
321+
At any point in time while executing Core WebAssembly code or a [canonical
322+
built-in] called by Core WebAssembly code, there is a well-defined **current
323+
thread** whose containing task is the **current task**. The "current thread" is
324+
modelled in the Canonical ABI's Python code by explicitly passing a [`Thread`]
325+
object as an argument to all function calls so that the semantic "current
326+
thread" is always the value of the `thread` parameter. Threads store their
327+
containing task so that the "current task" is always `thread.task`.
328+
329+
### Thread-Local Storage
330+
331+
Each thread contains a distinct mutable **thread-local storage** array. The
332+
current thread's thread-local storage can be read and written from core wasm
333+
code by calling the [`context.get`] and [`context.set`] built-ins.
334+
335+
The thread-local storage array's length is currently fixed to contain exactly
336+
2 `i32`s with the goal of allowing this array to be stored inline in whatever
337+
existing runtime data structure is already efficiently reachable from ambient
338+
compiled wasm code. Because module instantiation is declarative in the
339+
Component Model, the imported `context.{get,set}` built-ins can be inlined by
340+
the core wasm compiler as-if they were instructions, allowing the generated
341+
machine code to be a single load or store. This makes thread-local storage a
342+
natural place to store:
343+
1. a pointer to the linear-memory "shadow stack" pointer
344+
2. a pointer to a struct used by the runtime to implement the language's
345+
thread-local features
346+
347+
When threads are created explicitly by `thread.new-indirect`, the lifetime of
348+
the thread-local storage array ends when the function passed to
349+
`thread.new-indirect` returns and thus any linear-memory allocations associated
350+
with the thread-local storage array should be eagerly freed by guest code right
351+
before returning. Similarly, since each call to an export logically creates a
352+
fresh thread, thread-local allocations can be eagerly released when this
353+
implicit thread exits by returning from the exported function or, if the
354+
stackless async ABI is used, returning the "exit" code to the event loop. This
355+
non-reuse of thread-local storage between distinct export calls avoids what
356+
would otherwise be a likely source of TLS-related memory leaks.
357+
358+
When [memory64] is integrated into the Component Model's Canonical ABI,
359+
`context.{get,set}` will be backwards-compatibly relaxed to allow `i64`
360+
pointers (overlaying the `i32` values like hardware 32/64-bit registers). When
361+
[wasm-gc] is integrated, these integral context values can serve as indices
362+
into guest-managed tables of typed GC references.
363+
364+
Since the same mutable thread-local storage cells are shared by all core wasm
365+
running under the same thread in the same component, the cells' contents must
366+
be carefully coordinated in the same way as native code has to carefully
367+
coordinate native ISA state (e.g., the [FS or GS segment base address]). In the
368+
common case, thread-local storage is only `context.set` by the entry trampoline
369+
invoked by [`canon_lift`] and then all transitively reachable core wasm code
370+
(including from any `callback`) assumes `context.get` returns the same value.
371+
Thus, if any *non*-entry-trampoline code calls `context.set`, it is the
372+
responsibility of *that code* to restore this default assumption before
373+
allowing control flow to escape into the wild.
374+
375+
For more information, see [`context.get`] in the AST explainer.
376+
378377
### Streams and Futures
379378

380379
Streams and Futures have two "ends": a *readable end* and *writable end*. When

design/mvp/Explainer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3271,7 +3271,7 @@ For some use-case-focused, worked examples, see:
32713271
[Current Thread]: Concurrency.md#current-thread-and-task
32723272
[Current Task]: Concurrency.md#current-thread-and-task
32733273
[Thread-Local Storage]: Concurrency.md#thread-local-storage
3274-
[Subtask]: Concurrency.md#structured-concurrency
3274+
[Subtask]: Concurrency.md#subtasks-and-supertasks
32753275
[Stream or Future]: Concurrency.md#streams-and-futures
32763276
[Readable and Writable Ends]: Concurrency.md#streams-and-futures
32773277
[Readable or Writable End]: Concurrency.md#streams-and-futures

0 commit comments

Comments
 (0)