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
I create this issue to track implementation of the WASI threads proposal, define technical approach and give opportunity for discussion.
High-level approach
The high-level approach for implementing WASI threads is to create a new module instance for every new thread and only share the memory between created instances.
API
As of today there's a single API that has to be added to the host:
int32_tthread_spawn(void*start_arg)
where start_arg is an opaque pointer and its value should not be inspected by the host environment; instead, it should be passed back to the WASM code. The function return unique non-negative thread identifier integer. thread_spawn instantiates new thread, and must call the following exported function in a new thread once the thread is ready:
where the start_arg is exactly the same value as the one being passed to the thread_spawn, and thread_id is the same thread ID as the one returned from thread_spawn.
Project structure
We considered two approaches:
implement proposal in libc-wasi, given it is already part of wasi sdk
create a new library (in iwasm/core/libraries) that includes the implementation
The main benefit of the former approach is consistency with other APIs; however, we decided to go for the latter approach due to the following reasons:
WASI threads is still in progress and some of the concepts haven't been fully clarified yet; having it as a separate library help emphasizing the instability of the module to potential users
conditional compilation of the module won't require too many ifdef's here and there as the whole implementation will live in a separate place; that will enable us to have cleaner and more maintainable code
Our goal as part of wasi threads support is to provide a replacement for pthread library (through wasi-libc) therefore we won't guarantee compatibility with pthread library (i.e. both wasi threads and pthread library can not be used at the same time). If we see that using both implementations at the same time might actually be harmful, we might even restrict enabling both features at the same time at build time.
Users who are willing to use threads, should be instructed to use wasi-libc implementation on top of wasi threads in WAMR, so eventually pthread library can be deprecated and removed.
We see a value in keeping wasm_runtime_spawn_thread in parallel to wasi threads as this allows running concurrently multiple instances of a module from the native code (as opposed to spawning new threads from WASM code itself). wasi threads implementation should be able to work in parallel with wasm_runtime_spawn_thread functionality (with caveats, see sections below).
Thread instantiation & management
wasi threads implementation needs some sort of native thread management for multiple reasons, e.g.:
exception propagation - whenever the exception is being raised (e.g. out of bound memory access or division by zero), all the running threads should be notified and stopped
there's a discussion whether wasi_thread_exit-like API is needed in Is wasi_thread_exit needed? WebAssembly/wasi-threads#7; if so, or if other syscalls are being added for threads (e.g. for setting thread priorities), thread manager must be able to locate the thread and interact with it accordingly.
WAMR already have a concept of WASM threads implemented using pthread library and thread management using thread manager. We'll use this thread manager for prototype as it gives us out of the box thread instantiation. However, because thread manager also enforces aux stack management, we'll have to refactor the module to allow pass aux stack control to the application.
Auxiliary stack management
Currently WAMR manages stack boundaries when threads are enabled:
Based on the maximum number of threads, WAMR segments aux stack so segments can be assigned to the thread code
When a new thread is created (here or here), one of the segments is being assigned to the thread using allocate_aux_stack method.
When thread finishes, stack segment has to be released using free_aux_stack so new threads can use the segment.
The approach is not ideal for a few reasons:
stack size, configured e.g. by -z stack-size=XXX is split across all the threads, therefore e.g. the main thread receives only a fraction of the stack size. This is inconsistent with LLD's -Wl,-stack_size, where the thread receives a full stack_size value.
WAMR's stack allocation might interfere with user space stack allocation; for example, wasi-libc's pthread_create (similarly to musl's pthread_create) allocates memory for the stack dynamically, so there's no need for the host to manage stack for multiple threads.
We'll disable stack allocation for new threads created with wasi_thread_spawn and instead, we off-load stack management to WASM application.
Similar approach can be used for wasm_runtime_spawn_thread; we'll use wasm_runtime_module_malloc/wasm_runtime_module_free to allocate aux stack either in libc heap (if malloc/free are exported) or app heap.
Stack overrun detection
At the moment WAMR has the information about stack boundaries so can detect aux stack overflow/underflow when it happens. This detection still will be possible for wasm_runtime_spawn_thread because the memory for those methods will be allocated in native code. However, WAMR won't have access to stack boundaries defined by wasi-libc's pthread implementation. This was already discussed in WebAssembly/wasi-threads#12; one of the approach is to use binaryen's stack-check pass.
I create this issue to track implementation of the
WASI threadsproposal, define technical approach and give opportunity for discussion.High-level approach
The high-level approach for implementing
WASI threadsis to create a new module instance for every new thread and only share the memory between created instances.API
As of today there's a single API that has to be added to the host:
where
start_argis an opaque pointer and its value should not be inspected by the host environment; instead, it should be passed back to the WASM code. The function return unique non-negative thread identifier integer.thread_spawninstantiates new thread, and must call the following exported function in a new thread once the thread is ready:where the
start_argis exactly the same value as the one being passed to thethread_spawn, andthread_idis the same thread ID as the one returned fromthread_spawn.Project structure
We considered two approaches:
libc-wasi, given it is already part of wasi sdkiwasm/core/libraries) that includes the implementationThe main benefit of the former approach is consistency with other APIs; however, we decided to go for the latter approach due to the following reasons:
WASI threadsis still in progress and some of the concepts haven't been fully clarified yet; having it as a separate library help emphasizing the instability of the module to potential usersifdef's here and there as the whole implementation will live in a separate place; that will enable us to have cleaner and more maintainable codeCompatibility
WAMR already allows for spawning new threads, either using pthread library or wasm_runtime_spawn_thread.
Our goal as part of wasi threads support is to provide a replacement for pthread library (through wasi-libc) therefore we won't guarantee compatibility with pthread library (i.e. both wasi threads and pthread library can not be used at the same time). If we see that using both implementations at the same time might actually be harmful, we might even restrict enabling both features at the same time at build time.
Users who are willing to use threads, should be instructed to use wasi-libc implementation on top of wasi threads in WAMR, so eventually pthread library can be deprecated and removed.
We see a value in keeping
wasm_runtime_spawn_threadin parallel to wasi threads as this allows running concurrently multiple instances of a module from the native code (as opposed to spawning new threads from WASM code itself). wasi threads implementation should be able to work in parallel withwasm_runtime_spawn_threadfunctionality (with caveats, see sections below).Thread instantiation & management
wasi threads implementation needs some sort of native thread management for multiple reasons, e.g.:
wasi_thread_exit-like API is needed in Iswasi_thread_exitneeded? WebAssembly/wasi-threads#7; if so, or if other syscalls are being added for threads (e.g. for setting thread priorities), thread manager must be able to locate the thread and interact with it accordingly.WAMR already have a concept of WASM threads implemented using pthread library and thread management using thread manager. We'll use this thread manager for prototype as it gives us out of the box thread instantiation. However, because thread manager also enforces aux stack management, we'll have to refactor the module to allow pass aux stack control to the application.
Auxiliary stack management
Currently WAMR manages stack boundaries when threads are enabled:
The approach is not ideal for a few reasons:
-z stack-size=XXXis split across all the threads, therefore e.g. the main thread receives only a fraction of the stack size. This is inconsistent with LLD's-Wl,-stack_size, where the thread receives a fullstack_sizevalue.pthread_create) allocates memory for the stack dynamically, so there's no need for the host to manage stack for multiple threads.We'll disable stack allocation for new threads created with
wasi_thread_spawnand instead, we off-load stack management to WASM application.Similar approach can be used for
wasm_runtime_spawn_thread; we'll usewasm_runtime_module_malloc/wasm_runtime_module_freeto allocate aux stack either in libc heap (ifmalloc/freeare exported) or app heap.Stack overrun detection
At the moment WAMR has the information about stack boundaries so can detect aux stack overflow/underflow when it happens. This detection still will be possible for
wasm_runtime_spawn_threadbecause the memory for those methods will be allocated in native code. However, WAMR won't have access to stack boundaries defined by wasi-libc's pthread implementation. This was already discussed in WebAssembly/wasi-threads#12; one of the approach is to use binaryen's stack-check pass.MVP Tasks
thread_spawnfunction Add implementation for wasi_thread_spawn() #1786proc_exitterminates all of the threads, and traps kill the process