A proposed WebAssembly System Interface API to add native thread support.
NOTE: this proposal is considered a legacy proposal, retained for engines that can only support WASI v0.1 (
preview1). After much debate, future work on threads will happen in the shared-everything-threads proposal which adds component model built-ins for thread spawning, among other things. The goal is that WASI v0.2 and following will use shared-everything-threads (once fully implemented) and this proposal can eventually be removed. In the meantime, users experimenting with this proposal can continue to get help with questions and bugs by opening issues on this repository and tagging various maintainers who plan to continue supporting WASI v0.1 (e.g., @loganek, @yamt, @wenyongh).
Current Phase
Phase 1
Champions
Phase 4 Advancement Criteria
TODO before entering Phase 2.
Table of Contents
- Introduction
- Goals
- Non-goals
- API walk-through
- Detailed design discussion
- Considered alternatives
- Stakeholder Interest & Feedback
- References & acknowledgements
Introduction
This proposal looks to provide a standard API for thread creation. This is a WASI-level proposal that augments the WebAssembly-level threads proposal. That WebAssembly-level proposal provides the primitives necessary for shared memory, atomic operations, and wait/notify. This WASI-level proposal solely provides a mechanism for spawning threads. Any other thread-like operations (thread joining, locking, etc.) will use primitives from the WebAssembly-level proposal.
Some background: browsers already have a mechanism for spawning threads — Web Workers — and the WebAssembly-level proposal avoided specifying how thread spawning should occur. This allows other uses of WebAssembly — i.e., outside the browser — to specify their own mechanism for spawning threads.
Goals
-
pthreadssupport: the goal of this proposal is to add the missing functions that are required to implement a subset ofpthreadsAPI. It does not aim to be identical to thepthreadsAPI, but one must be able to create threads that operate on a shared Wasm memory while using the WebAssembly atomic instructions to synchronize on memory access. -
library reuse: standardizing this API would allow re-use of existing libraries and remove friction when porting projects from native execution contexts to WebAssembly and WASI environments (outside the browsers).
-
future-compatible: a possible future direction for WebAssembly is towards supporting multiple threads per instance. We aim to expose an API that would be compatible with this future direction.
-
browser polyfills: for browsers, we aim to provide a way to polyfill this API using Web Workers providing similar functionality to what exists in browsers today.
Non-goals
-
full POSIX compatibity: this API will not be 100% compatible with all functions and options described by POSIX threads standard.
-
modify core WebAssembly: the current proposal is limited to the WASI APIs signatures and behavior and does not propose changes to the Wasm instruction set.
API walk-through
The API consists of a single function. In pseudo-code:
status wasi_thread_spawn(thread_start_arg* start_arg);
where the status is a unique non-negative integer thread ID (TID) of the new
thread (see Design choice: thread IDs) or a
negative number representing an error if the host failed to spawn the thread.
The host implementing wasi_thread_spawn will call a predetermined function
export (wasi_thread_start) in a new WebAssembly instance. Any necessary
locking/signaling/thread-local storage will be implemented using existing
instructions available in WebAssembly. Ideally, users will never use
wasi_thread_spawn directly but rather compile their threaded code from a
language that supports threads (see below).
Use case: support various languages
Using this API, it should be possible to implement threads in languages like:
- C, using the
pthreadslibrary (see the current work in wasi-libc) - Rust, as a part of the
stdlibrary (in the future, e.g., here)
The API should be able to support even more languages, but supporting these initially is a good starting point.
Use case: support thread-local storage
For languages that implement thread-local storage (TLS), the start argument can
contain a language-specific structure with the address and (potentially) the
length of a TLS memory region. The host WebAssembly engine will treat this
argument as an opaque pointer — it should not introspect these
language-specific details. In C, e.g., the start function should be a static
trampoline-like wrapper (exported as wasi_thread_start) that reads the actual
user start function out of the start argument and calls this after doing some
TLS bookkeeping (this is not much different than how C starts threads natively).
Detailed design discussion
Threads are tricky to implement. This proposal relies on a specific convention
in order to work correctly. When instantiating a module which is expected to run
with wasi-threads, the WASI host must first allocate shared memories to
satisfy the module's imports.
Upon a call to wasi_thread_spawn, the WASI host must:
- instantiate the module again — this child instance will be used for the new thread
- in the child instance, import all of the same WebAssembly objects, including the above mentioned shared memories, as the parent
- optionally, spawn a new host-level thread (other spawning mechanisms are possible)
- calculate a positive, non-duplicate thread ID,
tid, and return it to the caller; any error in the previous steps is indicated by returning a negative error code. - in the new thread, call the child instance's exported entry function with the
thread ID and the start argument:
wasi_thread_start(tid, start_arg)
A WASI host that implements the above should be able to spawn threads for a variety of languages.
Design choice: thread IDs
When wasi_thread_spawn successfully spawns a thread, it returns a thread ID
(TID) — 32-bit integer with several restrictions. TIDs are managed and
provided by the WASI host. To avoid leaking information, the host may choose to
return arbitrary TIDs (as opposed to leaking OS TIDs).
Valid TIDs fall in the range