• Stars
    star
    111
  • Rank 303,240 (Top 7 %)
  • Language WebAssembly
  • Created almost 2 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

wasi-threads

A proposed WebAssembly System Interface API to add native thread support.

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 $[1, 2^{29})$. Some considerations apply:

  • 0 is reserved for compatibility reasons with existing libraries (e.g., wasi-libc) and must not be returned by wasi_thread_spawn
  • the uppermost three bits of a valid TID must always be 0. The most significant bit is the sign bit and recall that wasi_thread_spawn uses negative values to indicate errors. The remaining bits are reserved for compatibility with existing language implementations.

Design choice: termination

A wasi-threads module initially executes a single thread — the main thread. As wasi_thread_spawn is called, more threads begin to execute. Threads terminate in the following ways:

  • upon return from wasi_thread_start, and other threads continue to execute
  • upon a trap in any thread; all threads are immediately terminated
  • upon a proc_exit call in any thread; all threads are immediately terminated.

Design choice: pthreads

One of the goals of this API is to be able to support pthreads for C compiled to WebAssembly. Given a WASI host that implements thread_spawn as described above, what responsibility would the C language have (i.e., libc) to properly implement pthreads?

pthread_create must not only call WASI's wasi_thread_spawn but is also responsible for setting up the new thread's stack, TLS/TSD space, and updating the pthread_t structure. This could be implemented by the following steps (ignoring error conditions):

  1. configure a struct start_args with the user's void *(*start_func)(void *) and void *start_arg (as done natively) but also with pthread_t *thread
  2. call malloc (instead of mmap) to allocate TLS/TSD in the shared WebAssembly memory
  3. define a static, exported wasi_thread_start function that takes as parameters int tid and void *start_args
  4. in pthread_create, call wasi_thread_spawn with the configured start_args and use atomic.wait to wait for the start_args->thread->tid value to change (note that for web polyfills this may not be necessary since creation of web workers is not synchronous)
  5. now in the child thread: once the WASI host creates the new thread instance and calls wasi_thread_start, then a) set args->thread->tid to the host-provided tid, b) set the __wasilibc_pthread_self global to point to args->thread (this is used by pthread_self, e.g.), c) use atomic.notify to inform the parent thread that the child now has a tid, d) start executing the user's start_func with the user's start_arg — at this point the new instance is executing separately in its own thread
  6. back in the parent thread: once it has been notified that the child has recorded its TID, it can safely return with the pthread_t structure properly filled out.

pthread_join has a similar wait/notify implementation, but in reverse: the parent thread can wait on the thread->return address to change and the child thread can notify it of this once the user's start function finishes (i.e., at the end of the wasi_thread_start wrapper).

The remainder of the pthreads API can be split up into what can be implemented and what can safely be skipped until some later date.

What can easily be implemented
  • pthread_self can use the __wasilibc_pthread_self global to return the address to the current thread's pthread_t structure; this relies on each thread mapping to a new instance (and thus a new set of globals) &mdash see discussion below on "instance per thread."
  • pthread_detach can be implemented by using the flags already present in the pthread_t structure.
  • pthread_mutex_*, pthread_rwlock_*, pthread_cond_*, sem_* can all be implemented using existing operations in the WebAssembly threads proposal.
  • thread-specific data (TSD), i.e., functions using pthread_key_t, can be implemented using the memory region allocated for the thread in WebAssembly shared memory.
What can be skipped
  • pthread_yield is a deprecated pthreads function; sched_yield is the right one to use. Since it is unclear how WASI's scheduling should interact with the host's, this can be deferred until someone has a use case for it.
  • pthread_cancel allows a parent thread to cancel a child thread; in particular, asynchronous cancellation is difficult (impossible?) to implement without a WebAssembly mechanism to interrupt the child thread and it complicates the entire implementation. It can be left for later.
What has been implemented

wasi-libc contains an implementation of pthreads using wasi-threads. The implementation is currently in progress: see the list of threads-related PRs.

Design choice: instance-per-thread

A thread spawning mechanism for WebAssembly could be implemented in various ways: the way chosen here, a cloned "instance-per-thread," is one option. The other major option is to share the instance among many threads, as described in the Weakening WebAssembly paper. Sharing an instance among many threads, as described there, would require:

  • WebAssembly objects (memories, tables, globals, functions) to allow a shared attribute
  • the WebAssembly specification to grow a fork instruction

The "instance-per-thread" approach was chosen here because a) it matches the thread instantiation model of the browser (also "instance-per-thread") and b) the WebAssembly specification changes required for the other approach may take some time to materialize. In the meantime, this proposal allows threaded WebAssembly to progress. If in the future the WebAssembly specification were to add a "many-threads-per-instance" mechanism, the hope is that the API here should not need to change significantly, though it is unclear how much the changes might be.

The "instance-per-thread" approach chosen here does have its disadvantages:

  • higher memory consumption (each instance is cloned)
  • breaking behavior on non-standard functions such as dlopen() that require to modify the function table
  • potential breaking behaviour of existing binaries once a new instruction gets added. This is a low risk because shared attributes do not yet exist on globals/tables/etc. having the shared attribute in a future WebAssembly spec version is not a likely approach. Most likely, no attributes would be interpreted as local/private as that would keep the existing behavior for binaries.

Considered alternatives

Alternative: WebAssembly threads

Instead of exposing threads at the WASI level, thread spawning could be specified in the WebAssembly specification. This is the approach described in the Weakening WebAssembly paper. See the Design choice: instance-per-thread discussion above for more details.

Alternative: wasi-parallel

wasi-parallel is another WASI proposal which provides a parallel "for" construct, similar to what, e.g., OpenMP provides. wasi-parallel spawns N threads at a time (though they may not all run concurrently); this API spawns a single thread at a time.

Stakeholder Interest & Feedback

TODO before entering Phase 3.

References & acknowledgements

Many thanks for valuable feedback and advice from (alphabetical order):

More Repositories

1

design

WebAssembly Design Documents
11,261
star
2

binaryen

Optimizer and compiler/toolchain library for WebAssembly
WebAssembly
7,048
star
3

wabt

The WebAssembly Binary Toolkit
C++
6,350
star
4

WASI

WebAssembly System Interface
Rust
4,510
star
5

spec

WebAssembly specification, reference interpreter, and test suite.
WebAssembly
3,061
star
6

wasi-sdk

WASI-enabled WebAssembly C/C++ toolchain
Shell
1,097
star
7

gc

Branch of the spec repo scoped to discussion of GC integration in WebAssembly
WebAssembly
911
star
8

proposals

Tracking WebAssembly proposals
849
star
9

component-model

Repository for design and specification of the Component Model
Python
827
star
10

wasi-libc

WASI libc implementation for WebAssembly
C
784
star
11

interface-types

WebAssembly
640
star
12

threads

Threads and Atomics in WebAssembly
WebAssembly
577
star
13

wasm-c-api

Wasm C API prototype
C++
521
star
14

simd

Branch of the spec repo scoped to discussion of SIMD in WebAssembly
WebAssembly
519
star
15

meetings

WebAssembly meetings (VC or in-person), agendas, and notes
HTML
438
star
16

wasi-nn

Neural Network proposal for WASI
380
star
17

esm-integration

ECMAScript module integration
WebAssembly
341
star
18

wasm-jit-prototype

Standalone VM using LLVM JIT
C++
307
star
19

tool-conventions

Conventions supporting interoperatibility between tools working with WebAssembly.
276
star
20

website

WebAssembly website
CSS
269
star
21

testsuite

Mirror of the spec testsuite
WebAssembly
172
star
22

memory64

Memory with 64-bit indexes
WebAssembly
168
star
23

reference-types

Proposal for adding basic reference types (anyref)
WebAssembly
161
star
24

wasi-crypto

WASI Cryptography API Proposal
Makefile
160
star
25

wasi-sockets

WASI API proposal for managing sockets
Rust
155
star
26

exception-handling

Proposal to add exception handling to WebAssembly
WebAssembly
141
star
27

wasi-filesystem

Filesystem API for WASI
140
star
28

module-linking

Proposal for allowing modules to define, import and export modules and instances
WebAssembly
119
star
29

multi-memory

Multiple per-module memories for Wasm
WebAssembly
115
star
30

wasi-http

108
star
31

tail-call

Proposal to add tail calls to WebAssembly
WebAssembly
106
star
32

wasp

WebAssembly module decoder in C++
C++
105
star
33

stack-switching

A repository for the stack switching proposal.
WebAssembly
105
star
34

debugging

Design documents and discussions about debug support in WebAssembly
98
star
35

wasi-io

I/O Types proposal for WASI
96
star
36

wasmint

Library for interpreting / debugging wasm code
C++
93
star
37

function-references

Proposal for Typed Function References
WebAssembly
91
star
38

bulk-memory-operations

Bulk memory operations
WebAssembly
74
star
39

multi-value

Proposal to add multi-values to WebAssembly
WebAssembly
66
star
40

wasi-testsuite

WASI Testsuite
Rust
45
star
41

waterfall

Build and test bots
44
star
42

flexible-vectors

Vector operations for WebAssembly
WebAssembly
42
star
43

js-types

Proposal for adding type reflection to the JS API
WebAssembly
42
star
44

content-security-policy

WebAssembly
39
star
45

wasi-cloud-core

Rust
39
star
46

js-promise-integration

JavaScript Promise Integration
WebAssembly
37
star
47

relaxed-simd

Relax the strict determinism requirements of SIMD operations.
WebAssembly
36
star
48

wasi-webgpu

36
star
49

JS-BigInt-integration

JavaScript BigInt to WebAssembly i64 integration
WebAssembly
36
star
50

wasi-libc-old

Precursor to WASI libc.
C
35
star
51

stringref

WebAssembly
34
star
52

benchmarks

Resources for collaborative benchmarking
JavaScript
27
star
53

wasi-random

Entropy source API for WASI
25
star
54

wasi-keyvalue

24
star
55

wasi-clocks

Clocks API for WASI
22
star
56

wasi-sql

20
star
57

annotations

Proposal for Custom Annotation Syntax in the Text Format
WebAssembly
19
star
58

proposal-type-imports

Proposal for Type Imports & Exports
WebAssembly
19
star
59

memory-control

A proposal to introduce finer grained control of WebAssembly memory.
WebAssembly
19
star
60

shared-everything-threads

A draft proposal for spawning threads in WebAssembly
WebAssembly
18
star
61

constant-time

Constant-time WebAssembly
WebAssembly
18
star
62

wasi-sql-embed

18
star
63

wasi-proposal-template

Starter template for proposing a new WASI API
17
star
64

wasi-parallel

wasi-parallel is a proposal to add a parallel for construct to WASI.
Shell
17
star
65

sign-extension-ops

Sign-extension opcodes
WebAssembly
17
star
66

funclets

Proposal for adding funclets - flexible intraprocedural control flow
WebAssembly
17
star
67

mutable-global

Import & export of mutable globals
WebAssembly
16
star
68

wasi-tools

WASI tools
Rust
16
star
69

wasi-poll

16
star
70

wasi-messaging

messaging proposal for WASI
15
star
71

wasi-cli

Command-Line Interface (CLI) World for WASI
15
star
72

instrument-tracing

Proposal to add instrumentation and tracing instructions to WebAssembly
WebAssembly
14
star
73

nontrapping-float-to-int-conversions

Proposal to add non-trapping float-to-int conversions to WebAssembly
WebAssembly
14
star
74

extended-const

Proposal for extended constant expressions
WebAssembly
14
star
75

conditional-sections

WebAssembly
13
star
76

wasi-grpc

13
star
77

profiles

Profiles proposal
WebAssembly
12
star
78

wasi-logging

WASI logging API
11
star
79

extended-name-section

WebAssembly
11
star
80

feature-detection

WebAssembly
10
star
81

webassembly.github.io

Redirect to webassembly.org
HTML
10
star
82

wasi-blobstore

10
star
83

wasi-runtime-config

9
star
84

testsuite-js

WebAssembly testsuite tests converted to single-file JavaScript tests
JavaScript
9
star
85

lld

Staging repository for upstreaming WebAssembly support into lld
C++
9
star
86

branch-hinting

Proposal to add branch hinting functionality to WebAssembly
WebAssembly
9
star
87

decompressor-prototype

C++
8
star
88

wasi-observe

Observability World for WASI
Just
8
star
89

wat-numeric-values

Proposal to add numeric values to data segment definitions in the text format
WebAssembly
7
star
90

gc-js-customization

WebAssembly
7
star
91

wasi-classic-command

6
star
92

wasi-url

6
star
93

wasi-i2c

I2C API for WASi
Rust
5
star
94

wg-charter

Proposed WebAssembly Working Group charter
HTML
5
star
95

wit-abi-up-to-date

4
star
96

call-tags

WebAssembly
4
star
97

cg-charter

Proposed WebAssembly Community Group charter
HTML
4
star
98

root-scanning

Spec proposal for scanning/marking of linear memory GC roots
WebAssembly
4
star
99

js-string-builtins

JS String Builtins
WebAssembly
3
star
100

conditional-segment-initialization

Conditional segment initialization
WebAssembly
3
star