You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This template can be used to start a new proposal, which can then be proposed in the WASI Subgroup meetings.
4
-
5
-
The sections below are recommended. However, every proposal is different, and the community can help you flesh out the proposal, so don't block on having something filled in for each one of them.
6
-
7
-
Thank you to the W3C Privacy CG for the [inspiration](https://github.com/privacycg/template)!
8
-
9
-
# [Title]
10
-
11
-
A proposed [WebAssembly System Interface](https://github.com/WebAssembly/WASI) API.
3
+
A proposed [WebAssembly System Interface](https://github.com/WebAssembly/WASI) API to add native thread support.
12
4
13
5
### Current Phase
14
6
15
-
[Fill in the current phase, e.g. Phase 1]
7
+
Phase 1
16
8
17
9
### Champions
18
10
19
-
-[Champion 1]
20
-
-[Champion 2]
21
-
-[etc.]
11
+
-[Alexandru Ene](https://github.com/AlexEne)
12
+
-
22
13
23
14
### Phase 4 Advancement Criteria
24
15
25
-
TODO before entering Phase 2.
16
+
_TODO before entering Phase 2._
26
17
27
18
## Table of Contents [if the explainer is longer than one printed page]
28
19
@@ -31,69 +22,177 @@ TODO before entering Phase 2.
This proposal looks to provide a standard API that can be used for thread creation / join and the rest of the operations that are neccessary to run native threads (such as handling threadlocals, taking locks, spawning a thread).
45
35
46
-
[The "executive summary" or "abstract". Explain in a few sentences what the goals of the project are, and a brief overview of how the solution works. This should be no more than 1-2 paragraphs.]
36
+
### Goals
37
+
The goal of this proposal is to add the missing functions that are required for implementing a subset of `pthread` API. It doesn't aim to be identical to the pthreads API, but must be able to create threads that operate on the same Wasm memory, while using the atomic instrutions to synchronize on memory access.
47
38
48
-
### Goals [or Motivating Use Cases, or Scenarios]
39
+
Standardizing on this would allow re-use of existing libraries and code and remove friction from porting projects from native execution contexts to WebAssembly & WASI environments (outside the browsers).
49
40
50
-
[What is the end-user need which this project aims to address?]
41
+
A possible future direction for WebAssembly is towards supporting multiple-threads per instance. This isn't possible with the current memory model and instruction set. We aim to expose an API that would be compatible with this future direction.
42
+
43
+
For browsers, we aim to provide a way to polyfill these APIs, leaveraging web-workers, in a similar to how it works today.
51
44
52
45
### Non-goals
53
46
54
-
[If there are "adjacent" goals which may appear to be in scope but aren't, enumerate them here. This section may be fleshed out as your design progresses and you encounter necessary technical and other trade-offs.]
47
+
The goal of this API is not to be 100% compatible with all functions and options described by POSIX threads standard.
48
+
49
+
The current proposal is limited to the WASI APIs signatures and behavior and doesn't propose changes to the Wasm instruction set.
55
50
56
51
### API walk-through
57
52
58
-
[Walk through of how someone would use this API.]
53
+
The API requires the addition of a single function.
54
+
The mutex/signaling/TLS could be implemented using existing instructions available in WASM:
55
+
```
56
+
status thread_spawn(thread_id* thread_id, const thread_attributes* attrs, thread_start_func* function, thread_args* arg);
57
+
```
59
58
60
59
#### [Use case 1]
61
-
62
-
[Provide example code snippets and diagrams explaining how the API would be used to solve the given problem]
60
+
Implementing standard libraries on top of this API (e.g. Rust stdlib, pthreads).
63
61
64
62
#### [Use case 2]
65
-
66
-
[etc.]
63
+
Support for thread-local storage.
67
64
68
65
### Detailed design discussion
66
+
**TODO**:
67
+
* Define attributes supported.
68
+
* Clarify data types to match how other WASI methods are specified
69
+
* Does it need a stack_size parameter?
70
+
* Does it need instructions on how to set up the stack size?
71
+
72
+
#### How threads start
73
+
When a thread is started by `thread_spawn` the following happens:
74
+
1) The module instance is is cloned.
75
+
2) A native thread is created.
76
+
3) On that thread we call into a `_start_thread` function from the Wasm module created above and forwards the `arg` parameter (a pointer to the shared memory.
77
+
4)`_start_thread` Function then launches the target `function` with the `arg` parameter.
78
+
79
+
`pthread_create` can be implemented by forwarding a call to the new `thread_spawn` API.
80
+
```
81
+
int pthread_create(pthread_t* thread_id, const pthread_attr_t* attr, void* *(*start_routine)(void*), void* arg);
82
+
```
69
83
70
-
[This section should mostly refer to the .wit.md file that specifies the API. This section is for any discussion of the choices made in the API which don't make sense to document in the spec file itself.]
84
+
The following functions can potentially be implemented either by introducing new WASI APIs similar to, or by using WASM atomics (e.g. in the case of `pthread_join`):
85
+
```
86
+
int pthread_join(pthread_t thread, void **retval);
71
87
72
-
#### [Tricky design choice #1]
88
+
// Can this work without a new WASI API?
89
+
int pthread_detach(pthread_t thread);
90
+
91
+
// How would this work?
92
+
int pthread_cancel(pthread_t thread);
73
93
74
-
[Talk through the tradeoffs in coming to the specific design point you want to make.]
94
+
pthread_t pthread_self(void);
95
+
```
96
+
This is currently highlighted in [[Design choice 2]](#tricky-design-choice-2)
75
97
98
+
All the functions below can be implemented WASI libc with existing constructs available in the language and don't require a new WASI function:
76
99
```
77
-
// Illustrated with example code.
100
+
101
+
// This is really tricky to use right.
102
+
void pthread_exit(void *retval);
103
+
104
+
int pthread_yield(void);
78
105
```
79
106
80
-
[This may be an open question, in which case you should link to any active discussion threads.]
107
+
Mutexes:
108
+
```
109
+
int pthread_mutex_init(pthread_mutex_t *mutex, const void *attr);
81
110
82
-
#### [Tricky design choice 2]
111
+
int pthread_mutex_lock(pthread_mutex_t *mutex);
83
112
84
-
[etc.]
113
+
int pthread_mutex_unlock(pthread_mutex_t *mutex);
85
114
86
-
### Considered alternatives
115
+
int pthread_mutex_destroy(pthread_mutex_t *mutex);
87
116
88
-
[This section is not required if you already covered considered alternatives in the design discussion above.]
117
+
```
89
118
90
-
#### [Alternative 1]
119
+
RW Locks:
120
+
```
121
+
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
122
+
123
+
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
124
+
125
+
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
126
+
127
+
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
128
+
129
+
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
130
+
131
+
int pthread_rwlock_unlock();
132
+
133
+
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
134
+
```
91
135
92
-
[Describe an alternative which was considered, and why you decided against it.]
136
+
Conditionals:
137
+
```
138
+
int pthread_cond_init(pthread_cond_t *cond, const void *attr);
139
+
140
+
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
141
+
142
+
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, unsigned int useconds);
143
+
144
+
int pthread_cond_signal(pthread_cond_t *cond);
145
+
146
+
int pthread_cond_broadcast(pthread_cond_t *cond);
147
+
148
+
int pthread_cond_destroy(pthread_cond_t *cond);
149
+
```
150
+
151
+
Thread-specific data:
152
+
```
153
+
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
154
+
155
+
int pthread_setspecific(pthread_key_t key, const void *value);
156
+
157
+
void *pthread_getspecific(pthread_key_t key);
158
+
159
+
int pthread_key_delete(pthread_key_t key);
160
+
```
161
+
162
+
#### [Tricky design choice #1]
93
163
94
-
#### [Alternative 2]
164
+
This could be implemented either by cloning the current Wasm instance and executing it on another thread, or by having the instance shared amonst threads. Cloning the instance means that all the WASM constructs such as: Wasm globals (not C++ globals, these live in the Wasm linear memory, not the instance data), function tables will be thread local.
95
165
96
-
[etc.]
166
+
We consider this a good approach for the first implementation phase and aim to switch to a multiple threads per Wasm Instance once the `shared` attributes are added to the Wasm spec. Sharing the same instance right now is blocked on that attribute. More data on it can be found in this paper: [Weakening WebAssembly](https://www.researchgate.net/publication/336447205_Weakening_WebAssembly).
167
+
168
+
There are disadvantages to this approach of a thread gets its own module instance such as:
169
+
* Memory consumption (as each instance is cloned)
170
+
* Breaking behavior on non-standard functions such as `dlopen()` that require to modify the function table.
171
+
* Potential breaking behaviour of existing binaries once a new instruction gets added. This is low risk because no attributes on globals/tables/etc. having the meaning of `shared` in a future wasm spec iteration isn't a likely approach. Most likely, no attributes would be interpreted as `local`/`private` as that would keep the existing behavior for binaries.
172
+
173
+
The API here shouldn't need to change the signature if new annotations and instructions get added to the standard (e.g. `shared` and `local` flags on `globals`, `tables`, etc.). Regardless of those, the function exposed in this proposal will still take the same arguments and have the same return types in all of the potential execution modes. The function signature and observed behavior should stay the same (except dlopen behavior that is more restricted above).
174
+
175
+
#### [Tricky design choice #2]
176
+
While the following functions can potentially be implemented in wasm bytecode (is this true for `pthread_detatch`?), leaveraging the atomic operations available, it may be benefficial to have these functions included in the WASI proposal as this would aleviate bytecode size concerns for WASM binaries, performance concerns and can also potentially simplify some implementation details.
177
+
178
+
```
179
+
int pthread_join(pthread_t thread, void **retval);
*TODO(alexene) check that this understanding is correct*
195
+
The wasi-parallel proposal could be used in similar ways to [OpenMP](https://www.openmp.org/). That mode of parallelism solves a category of problems (map-reduce type algorithms are suited to such an approach), but can't be applied to other workloads that are covered in this proposal that require more fine-grained control over how threads are created/destroyed and their lifetimes.
97
196
98
197
### Stakeholder Interest & Feedback
99
198
@@ -103,8 +202,14 @@ TODO before entering Phase 3.
103
202
104
203
### References & acknowledgements
105
204
106
-
Many thanks for valuable feedback and advice from:
107
-
108
-
-[Person 1]
109
-
-[Person 2]
110
-
-[etc.]
205
+
Many thanks for valuable feedback and advice from (alphabetical order):
0 commit comments