GitHub

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

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

  • pthreads support: the goal of this proposal is to add the missing functions that are required to implement a subset of pthreads API. It does not aim to be identical to the pthreads API, 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 pthreads library (see the current work in wasi-libc)
  • Rust, as a part of the std library (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:

  1. instantiate the module again — this child instance will be used for the new thread
  2. in the child instance, import all of the same WebAssembly objects, including the above mentioned shared memories, as the parent
  3. optionally, spawn a new host-level thread (other spawning mechanisms are possible)
  4. 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.
  5. 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

Read the original on github.com ↗